Create OpenAPI spec for Django REST Framework APIs

Posted by Harald Nezbeda on Thu 14 July 2022

What is Open API and why you want to support it?

The OpenAPI Specification (OAS) defines a standard, programming language-agnostic interface description for HTTP APIs, which allows both humans and computers to discover and understand the capabilities of a service without requiring access to source code, additional documentation, or inspection of network traffic.

-- https://spec.openapis.org/oas/v3.0.3

In other words in is a documentation format that can be used in order to describe the capabilities of a REST API in a standardized format.

It solves the question of how to write documentation for a REST API at a technical level, and includes the definitions of all endpoints and actions, as well as the type of data for each request (body, URL parameters, header) and response.

During development, a lot of time is spent figuring out how to connect to and retrieve data from external services. However, since this document is standardized and machine-readable, it can be used to create the code for the communication layer.

Another advantage is versioning, which can be used to inform consumers about upcoming changes to the API. This is usually done with [Semantic Versioning] (https://semver.org/) and uses the same version as the software being documented.

How to generate documentation from code instead of writing it manually

In many frameworks, it seems that this task needs to be done manually or a lot of manual work is required to create the spec. This is not optimal, as such a workflow guarantees that the documentation will not be updated in a timely manner. The cause is not a lack of discipline, but rather confusion about who is responsible for the document, miscommunication with new team members, or simply lack of time when making quick fixes.

If we use Django and the Django REST framework, we can extract most of the data needed for OpenAPI. However, the framework has certain aspects that make this possible:

  • Django uses the data mapper pattern for the model definition. Which means that the full definition of the model is available in code.
  • DRF uses Django models for REST Endpoint generation
  • Standardized routing used by Django and DRF

Libraries

There are two major projects that I have used in the past to create Open API documentation:

I would encourage you to focus on version 3.0 for new projects. Open API 3.1 might become relevant in the future, but this has yet to be supported in the Python community as can be seen in OpenAPI tools.

Integrate DRF Spectacular

Make sure the package is installed:

pip install drf-spectacular[sidecar]

Sidecar is a self-contained distribution build of Swagger UI, which is pretty useful when combined with OpenAPI. In fact one might also consider to replace the Browsable API with, as the Swagger UI is capable of handling multiple request and response formats and also nested form elements.

After the installation some settings need to be defined

SPECTACULAR_SETTINGS = {
    "TITLE": "My project",
    "DESCRIPTION": "REST API of a cool project",
    "VERSION": "1.0.0",
    "SWAGGER_UI_DIST": "SIDECAR",
    "SWAGGER_UI_FAVICON_HREF": "SIDECAR",
}

and some new routes are required

from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView

urlpatterns = [
    #...
    # Rest Routes
    path('api/v1/', include([
        path('', include(router.urls)),
        path('schema/', include([
            path('', SpectacularAPIView.as_view(), name='schema'),
            # Optional UI:
            path('swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
        ])),
    ])),
    #...
]

Now the OpenAPI document should be available on /api/v1/schema and the Swagger UI (which loads the OpenAPI spec automatically) on /api/v1/swagger-ui.

For more details you may refer to this PR which contains the change required to add the spec generator in SnyPy.

In the next post I will focus on how to generate an API client form the existing OpenAPI Spec.