Build a Django REST API with Django REST Framework: A Step-by-Step Tutorial
Build a Django REST API with Django REST Framework: A Step-by-Step Tutorial
Building a REST API in Django is one of the most common tasks Python developers tackle. Whether you're powering a React frontend, a mobile app, or a data dashboard, Django REST Framework (DRF) gives you a structured and reliable way to expose your data over HTTP.
This tutorial walks through building a working API from scratch, including models, serializers, views, URL routing, and basic token authentication.
Prerequisites
You should be comfortable with basic Django concepts: models, views, and URL configuration. You'll need Python 3.10+ and a virtual environment.
pip install django djangorestframework
django-admin startproject myapi .
python manage.py startapp books
Add both rest_framework and books to INSTALLED_APPS in settings.py:
INSTALLED_APPS = [
# Django defaults...
'rest_framework',
'books',
]
Step 1: Define Your Model
For this tutorial, we're building an API around a simple Book model.
# books/models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
published_year = models.IntegerField()
isbn = models.CharField(max_length=13, unique=True)
def __str__(self):
return self.title
Run migrations:
python manage.py makemigrations
python manage.py migrate
Step 2: Create a Serializer
Serializers in DRF convert model instances to JSON (and back). A ModelSerializer does most of the heavy lifting automatically.
# books/serializers.py
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['id', 'title', 'author', 'published_year', 'isbn']
You can add custom validation here too. For example, to restrict future publication years:
def validate_published_year(self, value):
import datetime
if value > datetime.date.today().year:
raise serializers.ValidationError("Published year cannot be in the future.")
return value
Step 3: Write API Views
DRF gives you several ways to write views. APIView offers the most control; ViewSets with Routers reduce boilerplate. We'll start with a ModelViewSet, which handles list, create, retrieve, update, and delete in one class.
# books/views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from .models import Book
from .serializers import BookSerializer
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all().order_by('title')
serializer_class = BookSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
IsAuthenticatedOrReadOnly means anyone can read the data, but only authenticated users can create, update, or delete records. That's a sensible default for public-facing APIs.
Step 4: Configure URL Routing
DRF's Router automatically generates URL patterns for a ViewSet.
# books/urls.py
from rest_framework.routers import DefaultRouter
from .views import BookViewSet
router = DefaultRouter()
router.register(r'books', BookViewSet, basename='book')
urlpatterns = router.urls
Then include the books URLs in your project's main urls.py:
# myapi/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('books.urls')),
path('api-auth/', include('rest_framework.urls')), # Adds login/logout to browsable API
]
At this point, GET /api/books/ returns a list of books, and POST /api/books/ creates a new one.
Step 5: Add Token Authentication
For most APIs, you'll want token-based authentication rather than session cookies. DRF includes a built-in TokenAuthentication system.
Add rest_framework.authtoken to INSTALLED_APPS:
INSTALLED_APPS = [
...
'rest_framework.authtoken',
]
Run migrations again:
python manage.py migrate
Update your DRF settings in settings.py:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
],
}
Add an endpoint to obtain a token:
# myapi/urls.py
from rest_framework.authtoken.views import obtain_auth_token
urlpatterns = [
...
path('api/token/', obtain_auth_token, name='api_token_auth'),
]
Now POST a username and password to /api/token/ to receive an auth token:
curl -X POST http://127.0.0.1:8000/api/token/ \
-d "username=admin&password=yourpassword"
Response:
{"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"}
Include that token in subsequent requests:
curl -H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" \
http://127.0.0.1:8000/api/books/
Step 6: Test with the Browsable API
One of DRF's most useful features is the browsable API — an auto-generated HTML interface at any API endpoint. Start your dev server and visit http://127.0.0.1:8000/api/books/:
python manage.py runserver
You'll see a clean interface showing your data, available methods, and a form for POST requests. It's particularly useful during development and for onboarding teammates.
Step 7: Filter and Search
Real APIs need filtering. Install django-filter:
pip install django-filter
Add it to INSTALLED_APPS and update DRF settings:
REST_FRAMEWORK = {
...
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter',
],
}
Update your ViewSet to enable filtering and search:
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all().order_by('title')
serializer_class = BookSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = [DjangoFilterBackend, SearchFilter]
filterset_fields = ['author', 'published_year']
search_fields = ['title', 'author']
Now you can query like this:
GET /api/books/?author=Tolkien
GET /api/books/?search=ring
Actionable Takeaways
- Start with
ModelViewSetunless you need fine-grained control. It covers all CRUD operations with minimal code. - Use serializer validation to enforce business rules at the API boundary rather than in views.
- Token authentication is a straightforward starting point; consider
djangorestframework-simplejwtfor JWT-based flows as your project grows. django-filteris well worth adding early, as retrofitting filtering into an existing API is tedious.- The browsable API is a development convenience, but disable it in production by removing
rest_framework.renderers.BrowsableAPIRendererfrom your renderer classes if you don't want it exposed.
With these pieces in place: models, serializers, ViewSets, routing, and token auth, you have a production-worthy foundation to build on. From here, you can layer in pagination, throttling, nested serializers, or custom actions using DRF's @action decorator.