Filter concepts by levelShowing all levels.

Django · OpenAPI

OpenAPI and API Documentation

Schema generation from serializers, Swagger UI and ReDoc, documenting auth and examples, drf-spectacular vs drf-yasg

  • Generate the schema, never write ita generated document describes what you shipped, not what you meant to
  • Custom actions and errors are invisiblethey live in Python control flow — annotate them with @extend_schema
  • The security scheme must be declaredno generator can infer "Authorization: Bearer" from an auth class
  • Swagger UI is a working API clientgate it in production; ReDoc is read-only and reads better at scale
  • drf-yasg is OpenAPI 2.0 onlya specification limit, not a setting — no oneOf, no multiple examples

1.OpenAPI, schema generation, and request/response schemas

OpenAPI is a machine-readable description of an HTTP API — every path, every method, the shape of each request body, the shape of each response, and the authentication each endpoint expects — written as a single JSON or YAML document. Because it is machine-readable, one file drives several things at once: interactive documentation, generated client libraries in a dozen languages, request validation in a gateway, and contract tests. In DRF you do not write it by hand. A generator walks your URLconf, reads each view's serializer, and produces the document — which is why the schema is only as accurate as the serializers and the type hints it was derived from.

Key features

  • OpenAPI 3.x describes paths, operations, parameters, request bodies, responses, security schemes, and reusable components in one JSON/YAML document
  • DRF ships basic OpenAPI generation (`generateschema`, `rest_framework.schemas.openapi.AutoSchema`), and its own documentation recommends `drf-spectacular` for real use
  • Request and response schemas come from the serializer, so a serializer without declared types produces a schema with unknown types
  • A view returning a hand-built `Response({...})` has no serializer to read, so its response body is absent from the schema unless annotated
  • Error responses are invisible by default — the generator does not know which exceptions a view raises, so 400/403/409 shapes must be declared
  • Generate the document in CI and commit it: diffing it turns a breaking change into a reviewable line in a pull request

What the generator reads, and what it cannot see

What the generator reads, and what it cannot see
Part of the APIInferred fromAccurate by default?
Paths and methodsthe URLconf and the ViewSet's actionsyes
Request body`serializer_class`yes
Success response body`serializer_class`yes, for generic views
Query parametersthe filter backends and pagination classmostly — custom backends need annotating
Response of a custom `@action`nothing — it is plain Python**no** — annotate it
Error responses (400/403/409)nothing — exceptions are not static**no** — declare them
Auth requirementsthe authentication classesyes, once the security scheme is configured
From your code to a document everything else can consume

1 · The generator walks the URLconf

Every routed view becomes a path and a set of operations. Nothing here is written by hand, so nothing here can drift.

2 · Serializers become schemas

Field types, required-ness, choices, and read-only flags all transfer. A vague serializer produces a vague schema.

3 · You annotate what it cannot infer

Custom actions, error shapes, and dynamic serializer selection are Python control flow — invisible to a static pass until declared.

4 · One document, four consumers

Swagger UI and ReDoc render it, client generators compile it, a gateway validates against it, and CI diffs it to catch breaking changes.

  1. 1 · The generator walks the URLconf — Every routed view becomes a path and a set of operations. Nothing here is written by hand, so nothing here can drift.
  2. 2 · Serializers become schemas — Field types, required-ness, choices, and read-only flags all transfer. A vague serializer produces a vague schema.
  3. 3 · You annotate what it cannot infer — Custom actions, error shapes, and dynamic serializer selection are Python control flow — invisible to a static pass until declared.
  4. 4 · One document, four consumers — Swagger UI and ReDoc render it, client generators compile it, a gateway validates against it, and CI diffs it to catch breaking changes.

Command

python
REST_FRAMEWORK = {"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema"}
SPECTACULAR_SETTINGS = {"TITLE": "Orders API", "VERSION": "1.0.0"}

OpenAPI is a machine-readable description of the API, and in DRF it is generated from your URLconf and serializers rather than written. Generation makes it a correctness tool: it describes what you shipped, and a gap in the document is a gap in the code. But it can only read what is declarative — custom `@action` responses, dynamic serializer selection, and every error shape are invisible until annotated with `@extend_schema`. Commit the generated file and diff it in CI, so a breaking change becomes a line someone has to approve.

2.Swagger UI, ReDoc, documenting auth, examples, and versioning

One OpenAPI document, two common renderers. **Swagger UI** is interactive — it shows a "Try it out" button that sends a real request from the browser, which makes it excellent for exploring and risky to point at production. **ReDoc** is read-only, three-column, and much easier to read for a large API. Both are served from the same schema, so serving both costs one extra URL. Two things then need deliberate work, because a generator cannot infer either: the **security scheme** (declaring that endpoints take `Authorization: Bearer <jwt>`, so the docs let a reader authenticate and the generated clients know how), and **examples** — realistic request and response bodies, which are what turn a schema into something someone can actually copy and use. Each API version gets its own document.

Key features

  • Swagger UI executes real requests from the browser via "Try it out"; ReDoc renders the same document read-only
  • A security scheme must be declared (`SECURITY` / `AUTHENTICATION_WHITELIST` in `SPECTACULAR_SETTINGS`, or `extend_schema(auth=...)`) — the generator cannot infer the header convention from an authentication class
  • `OpenApiExample` attaches named request and response examples to an operation; several examples per operation is normal and useful
  • Realistic examples beat placeholder values — `"2026-09-04"` teaches the format, `"string"` teaches nothing
  • One document per API version, at its own URL: `/api/schema/?version=v2`, or a separate route per version
  • In production, gate the docs behind authentication or an internal network — Swagger UI is a fully functional API client pointed at your API

Swagger UI or ReDoc?

Swagger UI or ReDoc?
PropertySwagger UIReDoc
Sends real requestsyes — "Try it out"no
Best forexploring and debugging an endpointreading a large API end to end
Layoutone accordion columnthree columns, with a persistent nav
Auth entryan "Authorize" dialog that then signs requestsdisplays the requirement only
Production riska working API client on a public URLread-only, so much lower
Typical usestaging, or behind a loginpublic reference documentation
One document, two renderers, and the two things you must declare yourself

Swagger UI · /api/docs/

"Try it out" hits the live API

the fastest way to explore an endpoint

An Authorize dialog

works only if a security scheme is declared

Gate it in production

it is a working API client on a URL

ReDoc · /api/redoc/

Three columns, persistent nav

stays readable at a hundred endpoints

Shows examples side by side

which is what makes examples worth writing

Sends nothing

safe to publish

What you must add

The security scheme

bearerFormat: JWT — the header convention is not in the class

Realistic examples

"2026-09-04" teaches the format; "string" teaches nothing

One document per version

v1 and v2 are separate URLs, never merged

  • openapi.yaml
  • Swagger UI · /api/docs/ — interactive — it sends real requests
    • "Try it out" hits the live API — the fastest way to explore an endpoint
    • An Authorize dialog — works only if a security scheme is declared
    • Gate it in production — it is a working API client on a URL
  • ReDoc · /api/redoc/ — read-only reference
    • Three columns, persistent nav — stays readable at a hundred endpoints
    • Shows examples side by side — which is what makes examples worth writing
    • Sends nothing — safe to publish
  • What you must add — a generator cannot infer either of these
    • The security scheme — bearerFormat: JWT — the header convention is not in the class
    • Realistic examples — "2026-09-04" teaches the format; "string" teaches nothing
    • One document per version — v1 and v2 are separate URLs, never merged

Command

python
@extend_schema(
    examples=[OpenApiExample("A weekly export", value={"since": "2026-09-01", "format": "csv"})],
)

Swagger UI is interactive and therefore a working API client — gate it in production; ReDoc is read-only and reads better at scale. Both render the same generated document, so serving both costs one URL. Declare the security scheme yourself: the header convention is not derivable from an authentication class, and without it the Authorize button and every generated client are blind. Write realistic examples, because typical is what people copy. And give each API version its own document rather than merging them.

3.drf-spectacular, and drf-yasg in legacy codebases

Three tools generate schemas for DRF. **DRF's built-in** `AutoSchema` produces OpenAPI 3 and is deliberately minimal; DRF's own documentation points at third-party packages for anything real. **drf-spectacular** is the current default choice: OpenAPI 3.x, `@extend_schema` for everything the generator cannot infer, and a `--fail-on-warn` mode that turns "the generator had to guess here" into a CI failure. **drf-yasg** is the previous generation and produces OpenAPI **2.0** (Swagger) only — it is still widely deployed, so you will meet it, but it is not what you start a new project with. Its decorator is `@swagger_auto_schema`, which is the drf-spectacular `@extend_schema` equivalent.

Key features

  • drf-spectacular targets OpenAPI 3.x; drf-yasg targets OpenAPI 2.0 (Swagger) only, and cannot be configured otherwise
  • OpenAPI 3 adds `oneOf`/`anyOf`, reusable request bodies, multiple named examples, and a `servers` list — none expressible in 2.0
  • `@extend_schema` (spectacular) and `@swagger_auto_schema` (yasg) fill the same role: declaring what the generator cannot infer
  • `python manage.py spectacular --fail-on-warn` makes any generator guess a CI failure — the single most useful setting in this area
  • DRF's built-in `AutoSchema` exists and is minimal; the DRF documentation itself recommends third-party packages for production use
  • Migration is mechanical: replace the decorators, re-point the schema and docs URLs, then clear the warnings one by one

The three generators

The three generators
PropertyDRF built-in`drf-spectacular``drf-yasg`
OpenAPI version3.03.0 / 3.1**2.0 only**
Annotation decoratorsubclass `AutoSchema``@extend_schema``@swagger_auto_schema`
Multiple examples per operationnoyesno (2.0 limit)
`oneOf` / polymorphic fieldsnoyesno (2.0 limit)
CI gate on incomplete schemano`--fail-on-warn`no
Use it whena schema is barely neededany new projectyou inherited it
The same endpoint, annotated for each package

drf-spectacular — OpenAPI 3

  • Responses keyed by status code, each with its own serializer.
  • Several named examples per operation, split request/response.
  • oneOf for a field that is genuinely more than one shape.
  • --fail-on-warn turns a guessed schema into a failing build.
  • The default choice for anything new.

drf-yasg — OpenAPI 2.0

  • Same idea, same serializers, an older specification underneath.
  • One example per response — 2.0 has no syntax for more.
  • No oneOf, so a polymorphic field flattens to something less exact.
  • Still common in production; expect to meet it.
  • Migrating is mechanical, but it is a decorator sweep, not a setting.
  • drf-spectacular — OpenAPI 3
    • Responses keyed by status code, each with its own serializer.
    • Several named examples per operation, split request/response.
    • oneOf for a field that is genuinely more than one shape.
    • --fail-on-warn turns a guessed schema into a failing build.
    • The default choice for anything new.
  • drf-yasg — OpenAPI 2.0
    • Same idea, same serializers, an older specification underneath.
    • One example per response — 2.0 has no syntax for more.
    • No oneOf, so a polymorphic field flattens to something less exact.
    • Still common in production; expect to meet it.
    • Migrating is mechanical, but it is a decorator sweep, not a setting.

Command

bash
pip install drf-spectacular
# settings: DEFAULT_SCHEMA_CLASS = "drf_spectacular.openapi.AutoSchema"
python manage.py spectacular --file openapi.yaml --fail-on-warn

drf-spectacular for anything new — OpenAPI 3, `@extend_schema`, and `--fail-on-warn` so a guessed schema fails CI. drf-yasg is the previous generation and is limited to OpenAPI 2.0 by the specification, not by configuration, so it cannot express `oneOf`, reusable request bodies, or multiple examples. You will meet it in existing codebases; migrating is a mechanical decorator sweep, worth doing when the schema is load-bearing and easy to defer when it only renders a page.

Remember

  • Request and response schemas come from serializers, so a vague serializer produces a vague schema — the document is only as good as the declarations behind it.
  • Error responses are absent by default: the generator cannot trace which exceptions a view raises, and errors are the paths clients get wrong most.
  • One document per API version, at its own URL — merging versions makes the page unreadable and unusable by a client generator.
  • Realistic examples are what people copy: "2026-09-04" teaches the format, "string" teaches nothing.

Pro tip

  • Commit the generated openapi.yaml and run `git diff --exit-code` in CI — a breaking change becomes a line someone has to approve rather than something a client discovers.
  • Run `spectacular --fail-on-warn`: every warning marks a place the generator guessed, which is a place the document is quietly wrong.

Next up

  • Django Security
  • Caching
  • Django Testing

Sources

  • drf-spectacular — drf-spectacular.readthedocs.io
  • Every claim checked against drf-spectacular 0.27 and the OpenAPI 3.1 specification · checked 2026-09-04

Spotted a mistake on this page? Tell us.