Aman Xavier

Tutorials · 3 min read · 10 October 2026

Next.js frontend with a Django REST backend

The setup I use for BookMeDrive: Django REST Framework for the API, Next.js for the site, and the three settings that trip everyone up.

BookMeDrive runs on a Django REST API with a Next.js frontend. It's a good split: Django gives you the admin, the ORM and auth out of the box, and Next.js gives you fast pages that Google can read. Getting the two to talk takes about three settings. Here they are.

1. Expose an API from Django

Install Django REST Framework and django-cors-headers:

bash
pip install djangorestframework django-cors-headers

Then add them to settings. The CORS middleware needs to sit as high up the list as possible.

python
INSTALLED_APPS = [
    # ...
    "rest_framework",
    "corsheaders",
]

MIDDLEWARE = [
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.common.CommonMiddleware",
    # ... the rest
]

CORS_ALLOWED_ORIGINS = [
    "http://localhost:3000",
    "https://yourdomain.com",
]

A simple read-only endpoint:

python
# listings/views.py
from rest_framework import generics
from .models import Listing
from .serializers import ListingSerializer


class ListingList(generics.ListAPIView):
    queryset = Listing.objects.filter(is_active=True)
    serializer_class = ListingSerializer

2. Keep the API address in an environment variable

In the Next.js project, create .env.local:

bash
API_URL=http://localhost:8000
NEXT_PUBLIC_API_URL=http://localhost:8000

The difference matters. API_URL is only available on the server, which is where most of your data fetching should happen. Anything starting with NEXT_PUBLIC_ is baked into the browser bundle, so only use it for calls the browser has to make itself, and never put a secret in it.

3. Fetch on the server

In the App Router, a page can fetch data directly. Doing it on the server means the HTML arrives with the listings already in it, which is good for speed and for SEO.

tsx
// app/listings/page.tsx
type Listing = { id: number; title: string; city: string };

async function getListings(): Promise<Listing[]> {
  const res = await fetch(`${process.env.API_URL}/api/listings/`, {
    next: { revalidate: 60 }, // refresh at most once a minute
  });
  if (!res.ok) throw new Error("Couldn't load listings");
  return res.json();
}

export default async function ListingsPage() {
  const listings = await getListings();
  return (
    <ul>
      {listings.map((l) => (
        <li key={l.id}>
          {l.title}, {l.city}
        </li>
      ))}
    </ul>
  );
}

revalidate: 60 means the page is cached and rebuilt in the background at most once a minute, so Django isn't hit on every visit.

Skipping CORS with a rewrite

If you'd rather the browser never talks to Django directly, proxy it through Next.js. The browser calls /api/... on your own domain and Next.js forwards it, so there's no cross-origin request at all.

ts
// next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  async rewrites() {
    return [{ source: "/api/:path*", destination: `${process.env.API_URL}/api/:path*/` }];
  },
};

export default nextConfig;

Django adds a trailing slash to its URLs by default, which is why the destination ends with one.

Deploying the pair

  • Run Django behind gunicorn and a reverse proxy, with ALLOWED_HOSTS set to the API domain.
  • Set API_URL on your Next.js host to the production API address.
  • Add the production site to CORS_ALLOWED_ORIGINS if the browser calls the API directly.
  • If you use cookies for auth across the two domains, also look at CSRF_TRUSTED_ORIGINS and CORS_ALLOW_CREDENTIALS.

Work with me

Two ways to work together

If you run a business in Tricity

XavierStack builds websites, apps and booking systems for local businesses. That's the better door for you.

Go to xavierstack.com

If you're outside India

I take a few remote projects at a time. Django, Laravel, React, Next.js, and deployment, built end to end by the person who'll actually maintain it. I've shipped two live marketplaces, so I know what breaks once real users show up.

Projects start at $150. Also open to remote full-stack roles.

Tell me what you're building