In this guide, you’ll learn step by step how to set up Swagger and Redoc documentation for a Django REST Framework (DRF) API using drf-spectacular, one of the most robust OpenAPI generators available today.
API Documentation with Swagger in Django REST Framework

Clear, accurate, and always up-to-date API documentation is not optional; it’s a core part of building professional, scalable APIs. Whether you’re working solo, collaborating with frontend developers, or preparing your project for public consumption, Swagger-powered documentation makes your API easier to understand, test, and maintain.
If you are building an API with Django REST Framework, aka DRF, documentation isn't just a "nice-to-have"; it is essential. Good documentation makes your API discoverable, testable, and easier for frontend developers or third-party users to integrate with.
In the past, drf-yasg was the go-to tool, but strictly adhering to the OpenAPI 3.0 standard has made drf-spectacular the modern, community-recommended choice for Django developers.
Why Use Swagger for Your DRF API?
Swagger (OpenAPI) documentation provides:
-
📘 Automatic, live documentation synced with your code
-
🧪 Built-in API testing directly from the browser
-
🤝 Better collaboration between backend, frontend, and QA teams
-
🚀 Professional-grade APIs ready for production and scaling
Instead of manually writing docs that quickly become outdated, drf-spectacular derives the schema directly from your serializers, views, and URL configurations.
In this guide, we will walk through setting up auto-generated Swagger (and Redoc) documentation for your Django project in six easy steps.
Step 1: Install drf-spectacular
First, we need to install the library that generates the OpenAPI schema. We choose drf-spectacular because it is designed specifically for Django REST Framework, and it focuses on generating a schema that is explicitly OpenAPI 3.0 compliant, ensuring compatibility with Swagger UI, Redoc, and other API tools.
The Command:
pip install drf-spectacular
Best Practice: Always run this inside your active virtual environment. After installing, remember to update your requirements.txt (or Pipfile) by running pip freeze > requirements.txt so your teammates or deployment server can install the dependency.
Step 2: Register the App in Settings
Once installed, you must tell Django that this new app exists.
Update settings.py:
INSTALLED_APPS = [
# ... other apps
# Third-party apps
'rest_framework',
'drf_spectacular', # Add this line
]
Why do we do this? Adding it to INSTALLED_APPS allows Django to discover the module, load its management commands, and perform static file collections for the Swagger UI assets (CSS/JS files) when you eventually deploy.
Best practice
Always keep third-party apps clearly grouped in INSTALLED_APPS. This improves readability and helps new contributors understand your stack quickly.
Step 3: Configure DRF to use Spectacular
By default, Django REST Framework has its own basic schema generator. We need to override this and tell DRF to delegate all schema generation to drf-spectacular.
Tell Django REST Framework to use drf-spectacular as its schema generator.
Update settings.py:
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
How it works: The DEFAULT_SCHEMA_CLASS setting controls how DRF analyzes your views and serializers. By pointing this to AutoSchema, you are enabling the engine that inspects your code to automatically determine inputs, outputs, and validation constraints. Without this setting, DRF will continue using its default schema generator, and Swagger will not correctly detect your endpoints, serializers, or request/response formats.
Step 4: Customize Your API Metadata
While the defaults work, a professional API documentation needs a title, versioning, and description. This is configured via a dedicated settings dictionary.
Update settings.py:
# --- SWAGGER SETTINGS ---
SPECTACULAR_SETTINGS = {
'TITLE': 'My app API',
'DESCRIPTION': 'API documentation for my E-commerce Django REST project.',
'VERSION': '1.0.0',
'SERVE_INCLUDE_SCHEMA': False,
# Optional but very useful
'COMPONENT_SPLIT_REQUEST': True,
'SCHEMA_PATH_PREFIX': r'/api',
}
Explanation of key options Settings:
-
TITLE / DESCRIPTION / VERSION: These values appear directly at the very top of your Swagger UI documentation page. And it helps the user understand what this API is for.
-
COMPONENT_SPLIT_REQUEST: If your API handles different inputs for creation (POST) vs. patching (PATCH), this setting splits them into distinct definitions in the docs, making it much clearer for frontend devs. Separates request and response schemas, especially useful for POST and PUT endpoints.
-
SCHEMA_PATH_PREFIX: This filters the documentation to only show endpoints starting with /api, hiding internal URLs or admin routes from your public docs.
Best practice
Version your API documentation early. Even if you’re at 1.0.0, this sets the foundation for future breaking changes.
Step 5: Expose the Documentation URLs
Now that the backend is configured, we need to create the actual web pages where users can view the docs. We will set up three endpoints: the raw schema file, the Swagger UI, and Redoc.
Update urls.py:
from django.contrib import admin
from django.urls import path
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularSwaggerView,
SpectacularRedocView,
)
urlpatterns = [
path('admin/', admin.site.urls),
# ... your other url patterns
# --- Documentation Endpoints ---
# 1. OpenAPI Schema (YAML/JSON)
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
# 2. Swagger UI: Interactive testing interface
path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
# 3. Redoc UI: Clean, readable reference interface
path('api/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
]
Why two different UIs? Why expose multiple documentation UIs?
-
Swagger UI (/api/docs/): Best for developers. It allows you to execute requests, test endpoints, and authorize directly in the browser. Ideal for testing endpoints and sending requests.
-
Redoc (/api/redoc/): Best for readers. It provides a clean, 3-column layout that is easier to read but doesn't allow for executing requests. It looks more like a formal product manual. Clean, structured, and perfect for long-form API reference.
- Providing both gives developers flexibility.
Step 6: Run and Test
Everything is set. Run your development server to see the magic happen.
The Command:
python manage.py runserver
Now open your browser:
If everything is configured correctly, you’ll see all your API endpoints automatically documented, complete with request bodies, query parameters, authentication, and responses. You can now expand an endpoint, click "Try it out," and send real requests to your API without writing a single line of frontend code.
Best Practices for Production-Ready Swagger Docs
-
🔐 Protect docs in production (authentication or IP restrictions)
-
🏷️ Use serializer docstrings for clearer field descriptions
-
🧩 Group endpoints with tags for better navigation
-
📦 Document authentication schemes (JWT, session, token)
-
🔄 Regenerate docs automatically as part of CI/CD
Use Docstrings: drf-spectacular reads the docstrings of your ViewSets. If you want a description to appear under a specific endpoint in the UI, simply add a docstring to your view method:
def get(self, request):
"""
Returns a list of all active products in the inventory.
"""
...
Restrict Access: In a real production environment, you might not want the public to see your API structure. You can restrict the documentation views using standard DRF permissions (e.g., IsAdminUser) within the urls.py or by wrapping the views.
Bonus
Configuration snippet for adding JWT Authentication support
This setup allows you to paste your access token into Swagger UI so you can test protected endpoints directly in the browser.
Prerequisite
This guide assumes you are using djangorestframework-simplejwt, which is the standard for JWT in Django.
1. Update settings.py
You need to tell drf-spectacular that you are using JWT as a security scheme. Add the APPEND_COMPONENTS key to your existing SPECTACULAR_SETTINGS.
# settings.py
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
}
SPECTACULAR_SETTINGS = {
'TITLE': 'My app API',
'DESCRIPTION': 'API documentation for my E-commerce Django REST project.',
'VERSION': '1.0.0',
'SERVE_INCLUDE_SCHEMA': False,
# ... other settings ...
# --- SECURITY CONFIGURATION ---
# This configures the "Authorize" button
'COMPONENT_SPLIT_REQUEST': True,
'SECURITY': [{'Bearer': []}], # Default security scheme to use globally
'APPEND_COMPONENTS': {
'securitySchemes': {
'Bearer': {
'type': 'http',
'scheme': 'bearer',
'bearerFormat': 'JWT',
}
}
},
}
2. How to Use It in the UI
-
Restart your server: python manage.py runserver
-
Go to Swagger: http://localhost:8000/api/docs/
-
Get a Token: Use your login endpoint (e.g., /api/token/) to generate an access token.
-
Click "Authorize":
-
Click the green Authorize button at the top right of the Swagger page.
-
In the "Value" box, paste only the access token string.
-
Note: Standard Swagger "Bearer" schemes usually just need the token. If your backend requires the prefix "Bearer <token>", simplejwt usually handles the parsing, but strictly speaking, in this UI configuration, you often just paste the token code itself.
-
Click Authorize, then Close.
Now, when you "Try it out" on a protected endpoint, Swagger will automatically attach the header: Authorization: Bearer <your-token>.
Organizing Your API Documentation
By default, Swagger lists every endpoint alphabetically or by URL, which can get messy fast. To create a professional, navigable documentation site, you need to group related endpoints (e.g., "Products", "Orders", "Users") using Tags.
The standard way to do this in drf-spectacular is using the tags=['GroupName'] argument. Below is the cleanest implementation strategy for every type of Django View.
1. ViewSets (Group Everything at Once)
Best For: CRUD resources where one class handles List, Create, Retrieve, Update, and Delete. Strategy: Use @extend_schema(tags=...) on the class level. This automatically applies the tag to every action in the ViewSet.
from rest_framework import viewsets
from drf_spectacular.utils import extend_schema
@extend_schema(tags=['Products'])
class ProductViewSet(viewsets.ModelViewSet):
"""
Handles listing, creating, and updating products.
All endpoints here will appear under the "Products" group.
"""
queryset = Product.objects.all()
serializer_class = ProductSerializer
2. ListAPIView & RetrieveAPIView (Single Action Generics)
Best For: Simple, read-only endpoints. Strategy: Use @extend_schema_view on the class. This is safer than decorating methods because you don't need to override get().
from rest_framework import generics
from drf_spectacular.utils import extend_schema, extend_schema_view
# Group: "Orders" | Action: List
@extend_schema_view(
get=extend_schema(tags=['Orders'], summary="List all orders")
)
class OrderListView(generics.ListAPIView):
queryset = Order.objects.all()
serializer_class = OrderSerializer
# Group: "Orders" | Action: Retrieve (Details)
@extend_schema_view(
get=extend_schema(tags=['Orders'], summary="Get order details")
)
class OrderDetailView(generics.RetrieveAPIView):
queryset = Order.objects.all()
serializer_class = OrderSerializer
3. ListCreateAPIView (Multi-Action Generics)
Best For: Endpoints that handle two distinct actions (Get List + Create New). Strategy: Use @extend_schema_view to target get and post separately, but keep the same tag so they stay grouped together in the UI.
from rest_framework import generics
from drf_spectacular.utils import extend_schema, extend_schema_view
@extend_schema_view(
# The 'List' action
get=extend_schema(
tags=['Categories'],
summary="List categories"
),
# The 'Create' action
post=extend_schema(
tags=['Categories'],
summary="Create new category"
)
)
class CategoryListCreateView(generics.ListCreateAPIView):
queryset = Category.objects.all()
serializer_class = CategorySerializer
4. APIView (Manual Views)
Best For: Complex logic, non-model views, or "RPC-style" endpoints (e.g., triggering a report). Strategy: Decorate the specific methods (def get, def post) directly inside the class.
from rest_framework.views import APIView
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema
class DashboardStatsView(APIView):
@extend_schema(
tags=['Analytics'],
summary="Get daily stats",
description="Returns total sales and active user count for today."
)
def get(self, request):
data = {"sales": 100, "users": 50}
return Response(data)
@extend_schema(
tags=['Analytics'],
summary="Reset stats cache",
description="Clears the redis cache for dashboard metrics."
)
def delete(self, request):
# Logic to clear cache
return Response(status=204)
Summary Table
| View Type |
Recommended Tool |
Where to put it? |
| ViewSet |
@extend_schema(tags=['...']) |
On the Class |
| Generic Views |
@extend_schema_view(...) |
On the Class |
| APIView |
@extend_schema(...) |
On the Method (def get) |
What about third-party package
However, views like (simplejwt) comes from a third-party package, you cannot edit their source code to add decorators.
The best practice is to subclass them in your own views.py, add the documentation there, and then use your custom views in urls.py.
Step 1: Create Custom Views
In your views.py, import the original views and wrap them with @extend_schema_view.
# views.py
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from drf_spectacular.utils import extend_schema, extend_schema_view
@extend_schema_view(
post=extend_schema(
tags=['Authentication'],
summary="Login (Get Token)",
description="Takes a set of user credentials and returns an access and refresh JSON web token pair to prove the authentication of those credentials."
)
)
class CustomTokenObtainPairView(TokenObtainPairView):
pass
@extend_schema_view(
post=extend_schema(
tags=['Authentication'],
summary="Refresh Token",
description="Takes a refresh type JSON web token and returns an access type JSON web token if the refresh token is valid."
)
)
class CustomTokenRefreshView(TokenRefreshView):
pass
Step 2: Update urls.py
Switch the imports in your urls.py to point to your new Custom views instead of the default ones.
# urls.py
from django.urls import path
from .views import CustomTokenObtainPairView, CustomTokenRefreshView # Import from your views
urlpatterns = [
# ... other paths ...
path('token/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', CustomTokenRefreshView.as_view(), name='token_refresh'),
]
Why do this?
-
Grouping: Now these endpoints will appear under an "Authentication" tag in Swagger, separate from "Users" or "Products".
-
Clarity: The summaries "Login" and "Refresh Token" are much friendlier than the default generic names.
-
Future Proofing: If you ever need to customize the login logic (e.g., adding extra data to the response), you already have the class setup to do it.
Your final result may look like the following:

Final Thoughts
Setting up Swagger with drf-spectacular transforms your Django REST API from “working” to professional-grade. You get accurate documentation, faster onboarding, and a smoother development workflow, all with minimal configuration.
Once this foundation is in place, you can go even further by adding custom descriptions, examples, and authentication flows to create a truly polished API experience.
Happy documenting 🚀
Aucun commentaire