- Published on
How to Add Interactive Swagger Documentation with API Versioning to a Django Rest Framework API
- Authors
- Name
- Hussaini Usman
- @hmainatech
Introduction
An API is like a contract between a provider and a consumer. Adding versioning to the API makes sure that consumers know when things change or break.
While an interactive documentation makes it easier to interact with the API in a web browser.
In this article, I'm going to teach you every thing you need to know in order to start adding documentation and versioning to your DRF API.
Pre-requisite
To follow along with this tutorial, basic knowledge of building RESTful APIs with Django Rest Framework is required.
Swagger Documentation
We are going to start with adding swagger documentation to our DRF API. I created a sample project you can use to follow along if you don't have one.
Installing Dependencies
We'll be using drf-spectacular
, a django package that makes it easier to add swagger documentation and automatically generates json and yaml schema.
pip install drf-spectacular
Add drf_spectacular
to INSTALLED_APPS list
INSTALLED_APPS = [
# ALL YOUR APPS
'drf_spectacular',
]
Set DRF DEFAULT_SCHEMA_CLASS
to drf_spectacular.openapi.AutoSchema
REST_FRAMEWORK = {
# YOUR SETTINGS
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
drf-spectacular
comes pre-configured with some default settings, so you don't need to specify any settings before it works, but we are going customize it fit our use case. Have a look at the available settings.
SPECTACULAR_SETTINGS = {
'TITLE': 'My DRF API',
'DESCRIPTION': 'My Spectacular API',
'VERSION': '1.0.0',
'SERVE_INCLUDE_SCHEMA': False,
# OTHER SETTINGS
}
Usage
Add drf_spectacular
views to your project's urls.py
.
from django.urls import path, include
from drf_spectacular.views import SpectacularSwaggerView, SpectacularJSONAPIView
urlpatterns = [
# OTHER PATHS
path("schema.json", SpectacularJSONAPIView.as_view(), name="schema"),
path(
"docs/",
SpectacularSwaggerView.as_view(url_name="schema"),
name="swagger-ui",
),
]
Now when you visit /docs
page, you will see a generated swagger docs that is based on your seralizers.
An OpenAPI JSON schema is also available at /schema.json
, which you can use for multiple purposes, one of which is generating SDKs to interact with your API in almost any programming language.
If you're unable to access the docs, you'll need to install drf-spectacular[sidecar]
, you can read more about why you have to do that here.
pip install drf-spectacular[sidecar]
INSTALLED_APPS = [
# ALL YOUR APPS
'drf_spectacular',
'drf_spectacular_sidecar',
]
SPECTACULAR_SETTINGS = {
"TITLE": "My DRF API",
"DESCRIPTION": "My Spectacular API",
"VERSION": "1.0.0",
"SERVE_INCLUDE_SCHEMA": False,
"SWAGGER_UI_DIST": "SIDECAR",
"SWAGGER_UI_FAVICON_HREF": "SIDECAR",
"REDOC_DIST": "SIDECAR",
}
Customization
There are multiple ways you can customize the generated schema for an endpoint, but we will focus on using the extend_schema_view
and extend_schema
decorators. You can read learn more about customization here.
The following code will add description to our /smartphones/ endpoint.
from .serializers import PhoneSerializer
from .models import Phone
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from drf_spectacular.utils import extend_schema_view, extend_schema
@extend_schema_view(list=extend_schema(description="List of smartphones."))
class PhoneViewSet(GenericViewSet, ListModelMixin):
queryset = Phone.objects.all()
serializer_class = PhoneSerializer
You can also use extend_schema
to customize individual view methods.
class PhoneViewSet(GenericViewSet, ListModelMixin):
queryset = Phone.objects.all()
serializer_class = PhoneSerializer
@extend_schema(description="List of smartphones.")
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
Versioning
Django Rest Framework provides out of the box solution for adding versioning to an API, and there are 5 different ways you can set it up, which are referred to as 'versioning schemes' in the official documentation.
The 5 versioning schemes are:
1. URLPathVersioning
Expects the version to be include as part of the URL path.
GET /v1/smartphones/ HTTP/1.1
Host: myapi.com
Accept: application/json
2. NamespaceVersioning
Thesame as URLPathVersioning, the only difference is how it is configured using namespace.
3. AcceptHeaderVersioning
Expects the client to specify the version in the Accept
header.
GET /smartphones/ HTTP/1.1
Host: myapi.com
Accept: application/json; version=1.0
4. HostNameVersioning
Expects the client to specify the version as part of the hostname in the URL.
GET /smartphones/ HTTP/1.1
Host: v1.myapi.com
Accept: application/json
5. QueryParameterVersioning
Expects the client to specify the version as a query parameter in the URL path.
GET /smartphones/?version=1.0 HTTP/1.1
Host: myapi.com
Accept: application/json
In this tutorial we are going to show you how to setup URLPathVersioning, which I believe is the easiest to setup.
Configuration & Usage
REST_FRAMEWORK = {
# OTHER SETTINGS
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
"DEFAULT_VERSION": "v1",
"ALLOWED_VERSIONS": ["v1"],
}
urlpatterns = [
# OTHER URLS
path("v1/smartphones/", include("smartphones.urls")),
]
When you visit /v1/smartphones/, it'll return a list of smartphones just like before but with the version included in the path.
Now let's see how to add another version of our API, we'll call it v2
.
To achieve that, we are going to do the following:
- Create a new ViewSet which we're going to use for our /v2/smartphones/ endpoints.
- Rename
smartphones/urls.py
tosmartphones/v1_urls.py
. - Create a new file
smartphones/v2_urls.py
and add paths to it. - Add
v2
to ALLOWED_VERSIONS list.
from .serializers import PhoneSerializer
from .models import Phone
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from drf_spectacular.utils import extend_schema_view, extend_schema
@extend_schema_view(list=extend_schema(description="List of smartphones."))
class PhoneViewSet(GenericViewSet, ListModelMixin):
queryset = Phone.objects.all()
serializer_class = PhoneSerializer
class PhoneV2ViewSet(GenericViewSet, ListModelMixin, RetrieveModelMixin):
queryset = Phone.objects.all()
serializer_class = PhoneSerializer
@extend_schema(description="List of smartphones.")
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
@extend_schema(description="Retrieve a smartphone.")
def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)
from rest_framework.routers import DefaultRouter
from .views import PhoneV2ViewSet
from django.urls import path, include
router = DefaultRouter()
router.register("", PhoneV2ViewSet, basename="phones")
urlpatterns = [
path("", include(router.urls)),
]
Then include it in your project's project/urls.py
:
urlpatterns = [
# OTHER URLS
path("v1/smartphones/", include("smartphones.v1_urls")),
path("v2/smartphones/", include("smartphones.v2_urls")),
]
REST_FRAMEWORK = {
# OTHER SETTINGS
"ALLOWED_VERSIONS": ["v1", "v2"],
}
Now when you visit the docs page, you'll see 2 new smartphone endpoints grouped together under v2
.

What if we want to group our endpoints based on the respective apps they belong to, instead of the version string? All we have to do is add SCHEMA_PATH_PREFIX
to drf-spectacular settings
.
SPECTACULAR_SETTINGS = {
# Other Settings
"SCHEMA_PATH_PREFIX": "/v(?P<version>[0-9]+)/",
}

That's how you add interactive docs with versioning to your DRF API.
Source code for the final project is available on github.