Let’s use drf-spectacular to simplify creation of beautiful docs for your Django application according to the OpenAPI Specification version 3. Sounds easy? It wasn’t for me… Since I’m still a relatively newbie programmer, I constantly struggle with different topics and almost always Googling helps. But not in this case. I decided to add the Django REST framework into my toolchain and combine it with technologies like OAS 3.0. I saw how cool API documentation might look when I first discovered FastAPI and wanted to implement Swagger UI in my own app. The most popular option, in my opinion, was drf-yasg but it explicitly says that "drf-yasg is unlikely to soon, if ever, get support for OpenAPI 3.0". Well, the second-best option in this direction is drf-spectacular, but I couldn't figure out how to document my API just by following the provided examples (by the way I did it for the first time). The solution I came up with is to create documentation with standard DRF tools (easiest way), then generate OpenAPI 2 schemas with drf-yasg (since it has a richer description of the process) and finally tune the configuration to use . drf-spectacular Here I’m going to shorten this path for you by using example from the DRF documentation and sprinkle some customization on top of that to implement Swagger UI with version 3 of the specification. So, without further ado, be brave and follow me! quickstart Django REST framework We start with a couple of steps that are slightly different from the official DRF documentation. I’ll omit some details specific to the framework itself and if you feel lack of information for your own implementation, just explore the official site or ask a question in the comment section below. 1. Project setup First things first, create the project directory and enter it. mkdir tutorial tutorial cd Then check if pipenv is installed in order to create a virtual environment and isolate your package dependencies. pip3 install pipenv Install Django and Django REST framework into the virtual environment. pipenv install django pipenv install djangorestframework Activate virtual environment. pipenv shell Now, after a little preparation, it's time to set up a new project with a single application. Note the trailing "." character – it’s not a typo. django-admin startproject config . django-admin startapp quickstart As an intermediate check, let’s compare our project's layout which at this point should look like this. Now sync the database for the first time which will add a new SQLite file to the aforementioned layout. python manage.py migrate After successful migration, create an initial user with password . You’ll be prompted to enter the password blindly. admin password123 python manage.py createsuperuser --email admin@example.com --username admin 2. Serializers Roll up your sleeves because we're starting to code more! Inside your tutorial directory, run the following command to create a new file. serializers.py touch quickstart/serializers.py Open this newly created file with your favorite IDE and fill it with the following content. django.contrib.auth.models User, Group rest_framework serializers model = User fields = [ , , , ] model = Group fields = [ , ] from import from import : class UserSerializer (serializers.HyperlinkedModelSerializer) : class Meta 'url' 'username' 'email' 'groups' : class GroupSerializer (serializers.HyperlinkedModelSerializer) : class Meta 'url' 'name' 3. Views After that, open and get typing or just copy and paste – as you wish, no judgment 😉 quickstart/views.py django.contrib.auth.models User, Group rest_framework viewsets rest_framework permissions .serializers UserSerializer, GroupSerializer queryset = User.objects.all().order_by( ) serializer_class = UserSerializer permission_classes = [permissions.IsAuthenticated] queryset = Group.objects.all() serializer_class = GroupSerializer permission_classes = [permissions.IsAuthenticated] from import from import from import from import : class UserViewSet (viewsets.ModelViewSet) """ API endpoint that allows users to be viewed or edited. """ "-date_joined" : class GroupViewSet (viewsets.ModelViewSet) """ API endpoint that allows groups to be viewed or edited. """ Instead of writing multiple views, all common behavior is grouped into classes called ViewSets. For future purposes, you can easily break them down into individual views if needed, but using viewsets keeps the view logic nicely organized and very concise. 4. URLs It’s time to make API urls available. Navigate to the config folder and change content to the following. urls.py django.urls include, path rest_framework routers quickstart views router = routers.DefaultRouter() router.register( , views.UserViewSet) router.register( , views.GroupViewSet) urlpatterns = [ path( , include(router.urls)), path( , include( , namespace= )), ] from import from import from import r"users" r"groups" # Wire up our API using automatic URL routing. # Additionally, login URLs included for the browsable API. "" "api-auth/" "rest_framework.urls" "rest_framework" 5. Pagination Pagination allows you to control how many objects per page are returned. To enable it, add the following lines at the bottom of config/settings.py REST_FRAMEWORK = { : , : , } "DEFAULT_PAGINATION_CLASS" "rest_framework.pagination.PageNumberPagination" "PAGE_SIZE" 10 Actually, we don’t really need this setting unless you decide to populate the database with lots of data further down the road. 6. Settings Within the same file, for the test purposes, enable all hosts by adding "*" to ALLOWED_HOSTS. settings.py ALLOWED_HOSTS = [ ] "*" Also add "rest_framework" to INSTALLED_APPS. INSTALLED_APPS = [ ... , ] "rest_framework" 7. Testing the API It’s time to test what we’ve accomplished so far. Let’s launch the server. python manage.py runserver Now in your browser open the URL . You're probably seeing the page like this, which means you need to log in (top right corner) with the superuser credentials you've created at the end of step 1. http://127.0.0.1:8000/users/ After successful login, the page should look like this. If yes, great job! So far so good and let’s move on to the next step: changing this fairly classy UI to the awesome Swagger! drf-spectacular First, stop the server by pressing CONTROL-C. After this, you need to install drf-spectacular in your virtual environment. pipenv install drf-spectacular Then, in add drf-spectacular to the installed apps. By now the result should be like this. settings.py INSTALLED_APPS = [ , , , , , , , , ] "django.contrib.admin" "django.contrib.auth" "django.contrib.contenttypes" "django.contrib.sessions" "django.contrib.messages" "django.contrib.staticfiles" "rest_framework" "drf_spectacular" Finally, register AutoSchema with DRF by adding an extra line to the REST_FRAMEWORK settings also inside file so that the result looks like this. settings.py REST_FRAMEWORK = { : , : , : , } "DEFAULT_PAGINATION_CLASS" "rest_framework.pagination.PageNumberPagination" "PAGE_SIZE" 10 "DEFAULT_SCHEMA_CLASS" "drf_spectacular.openapi.AutoSchema" At the time of writing this text, the most recent version of drf-spectacular is 0.11.1. So, bear in mind that there might be some breaking changes in the code as it’s still below 1.x.x and under active development. OpenAPI 3.0 with Swagger UI And now the trickiest part, which was especially difficult for me. Hopefully, you'll avoid the misery I went through and jump straight to the happy part 🎊 1. URLs Switch to the inside config folder and modify content as follows. urls.py django.urls include, path rest_framework routers quickstart views drf_spectacular.views SpectacularAPIView, SpectacularSwaggerView router = routers.DefaultRouter() router.register( , views.UserViewSet) router.register( , views.GroupViewSet) urlpatterns = [ path( , include(router.urls)), path( , include( , namespace= )), path( , SpectacularAPIView.as_view(), name= ), path( , SpectacularSwaggerView.as_view( template_name= , url_name= ), name= , ), ] from import from import from import from import r"users" r"groups" # Wire up our API using automatic URL routing. # Additionally, login URLs included for the browsable API. "" "api-auth/" "rest_framework.urls" "rest_framework" # OpenAPI 3 documentation with Swagger UI "schema/" "schema" "docs/" "swagger-ui.html" "schema" "swagger-ui" Basically, you've just added a new import and two paths required for the Swagger UI. 2. Template Now let's create a folder for a custom API documentation template with a new file inside. I assume that your terminal stays in the same root directory this whole time – tutorial. mkdir templates touch templates/swagger-ui.html Then open and paste the following code. Just a quick note: I ran into difficulties even with this piece of code taken from the , so it's changed a bit although it may seem familiar. swagger-ui.html DRF documentation Swagger <!DOCTYPE html> < > html < > head < > title </ > title < = > meta charset "utf-8" < = = > meta name "viewport" content "width=device-width, initial-scale=1" < = = = > link rel "stylesheet" type "text/css" href "https://unpkg.com/swagger-ui-dist@3/swagger-ui.css" </ > head < > body < = > div id "swagger-ui" </ > div < = > script src "https://unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js" </ > script < > script ui = SwaggerUIBundle({ : , : , : [ SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset ], : , : { request.headers[ ] = request; } }) const url "{% url 'schema' %}" dom_id '#swagger-ui' presets layout "BaseLayout" requestInterceptor ( ) => request 'X-CSRFToken' "{{ csrf_token }}" return </ > script </ > body </ > html 3. Settings And again go back to inside config folder. Find TEMPLATES and change the line "DIRS" so that the whole block is as follows in order to tell Django where to look for our custom template. settings.py TEMPLATES = [ { : , : [BASE_DIR / ], : , : { : [ , , , , ], }, }, ] "BACKEND" "django.template.backends.django.DjangoTemplates" "DIRS" "templates" "APP_DIRS" True "OPTIONS" "context_processors" "django.template.context_processors.debug" "django.template.context_processors.request" "django.contrib.auth.context_processors.auth" "django.contrib.messages.context_processors.messages" Final check of the project layout just to make sure we are on the same page. After all those configurations, let's spin up the server and open the url http://127.0.0.1:8000/docs/ Lo and behold! Your Swagger UI documentation implemented in the OpenAPI 3.0 standard 🎉 Further enhancements are only up to you, but there are a couple of ideas: expand the range of available data formats, for example with or ; djangorestframework-xml djangorestframework-yaml use the @extend_schema decorator to add additional information to your views; override the default configuration by specifying in settings.py; SPECTACULAR_SETTINGS make a branded page by applying your own CSS to the docs template;