Overview
Modern web apps must allow users to manage their profile—update name, phone, etc.—securely and easily. In this blog post, you’ll learn how to:
✅ Set up an account page in Next.js
✅ Connect it with a Django backend API
✅ Use REST API to fetch and update user info
✅ Handle form submission and validation
✅ Secure endpoints using authentication
Tech Stack
- Django – Backend REST API using Django Rest Framework (DRF)
- Next.js 14 – Frontend (App Router)
- JWT Authentication (or Session-based)
- Axios for HTTP requests
- CSS Modules or Tailwind for styling
Folder Structure
/backend (Django)
└── accounts/
├── models.py
├── views.py
├── serializers.py
└── urls.py
/frontend (Next.js)
└── app/account/page.tsx
└── app/account/EditProfile.tsx
└── lib/api.ts
1. Django Setup (Backend)
1.1 Extend User Model
# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
phone = models.CharField(max_length=15, blank=True, null=True)
1.2 Create Serializer
# accounts/serializers.py
from rest_framework import serializers
from .models import CustomUser
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ['username', 'email', 'first_name', 'last_name', 'phone']
1.3 Create Views
# accounts/views.py
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
from .serializers import UserProfileSerializer
class UserProfileView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
serializer = UserProfileSerializer(request.user)
return Response(serializer.data)
def put(self, request):
serializer = UserProfileSerializer(request.user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
1.4 Add URLs
# accounts/urls.py
from django.urls import path
from .views import UserProfileView
urlpatterns = [
path("profile/", UserProfileView.as_view(), name="user-profile"),
]
And include this in your project urls.py
.
2. Next.js Frontend
2.1 Axios Setup
// lib/api.ts
import axios from "axios";
const api = axios.create({
baseURL: "http://localhost:8000/api", // your backend base URL
withCredentials: true, // if using session cookies
});
export default api;
2.2 Account Page (page.tsx)
"use client";
import { useEffect, useState } from "react";
import api from "@/lib/api";
import EditProfile from "./EditProfile";
interface User {
username: string;
email: string;
first_name: string;
last_name: string;
phone?: string;
}
export default function AccountPage() {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
api.get("/profile/")
.then((res) => setUser(res.data))
.catch(console.error);
}, []);
return (
<div className="account-container">
<h1>My Account</h1>
{user ? <EditProfile user={user} /> : <p>Loading...</p>}
</div>
);
}
2.3 Edit Profile Form (EditProfile.tsx)
"use client";
import { useState } from "react";
import api from "@/lib/api";
interface Props {
user: {
username: string;
email: string;
first_name: string;
last_name: string;
phone?: string;
};
}
export default function EditProfile({ user }: Props) {
const [form, setForm] = useState(user);
const [status, setStatus] = useState("");
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setForm({ ...form, [e.target.name]: e.target.value });
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
await api.put("/profile/", form);
setStatus("✅ Profile updated successfully");
} catch {
setStatus("❌ Failed to update profile");
}
};
return (
<form onSubmit={handleSubmit} className="form-profile">
<input name="first_name" value={form.first_name} onChange={handleChange} placeholder="First Name" />
<input name="last_name" value={form.last_name} onChange={handleChange} placeholder="Last Name" />
<input name="phone" value={form.phone || ""} onChange={handleChange} placeholder="Phone" />
<button type="submit">Save Changes</button>
{status && <p>{status}</p>}
</form>
);
}
3. Authentication Notes
You can use either:
- JWT Token Auth with
Authorization: Bearer <token>
header - Session-Based Auth (recommended for Next.js SSR pages using cookies)
In both cases:
- Protect Django views with
IsAuthenticated
- Send auth tokens via cookies or headers from frontend
API Summary
Endpoint | Method | Auth | Description |
---|---|---|---|
/api/profile/ | GET | Yes (user) | Fetch current user |
/api/profile/ | PUT | Yes (user) | Update user profile |
Final Outcome
You now have:
- A user-friendly account dashboard
- Secure, real-time profile editing
- A clean structure ready for extensions (password change, photo upload, etc.)