1. Basic concepts
OpenAPI Specification (OAS) is a set of standard specifications for describing HTTP API, and API can be defined using the YAML or JSON format.
The OpenAPI document describes the interface path, request parameters, request body, response data, authentication method, data model, etc.
The difference between OpenAPI, Swagger and Schema
| concept | Description |
|---|---|
| OpenAPI | The standard specification for describing HTTP API |
| Swagger UI | Generate interactive API documents from OpenAPI documents |
| Swagger Editor | Edit and validate OpenAPI documents |
| Swagger Codegen | Generate client or server code according to OpenAPI documentation |
| Schema | Schema definition describing API request, response and data structure |
OpenAPI itself is not a API framework, nor is it responsible for executing HTTP requests, but rather provides a machine-readable API contract.
Typical workflow:
Django REST Framework
↓
drf-spectacular
↓
OpenAPI Schema (YAML / JSON)
↓
┌────┴───────┐
↓ ↓
Swagger UI Orval
↓ ↓
API Documentation TypeScript Type
API Client / Hooks
In this way, the front and back ends can share the same API definition, reducing the problem of interface field inconsistency.
2. OpenAPI document structure
Main objects in a OpenAPI document:
| object | function | Is it necessary |
|---|---|---|
| OpenAPI Object | The root object containing the entire API definition | Yes |
| Info Object | API name, version, description and other metadata | Yes |
| Paths Object | API paths and HTTP operations | Yes |
| Servers Object | API server addresses | No |
| Components Object | Reusable Schema, parameters, responses, and more | No |
| Tags Object | Logical grouping of API operations | No |
| ExternalDocs Object | Links to external documentation | No |
| Security Requirement Object | Statement of security certification requirements for API | No |
Note: security is a field in the OpenAPI root object or Operation Object, not an independent top-level object name at the same level as paths and components.
Minimal documentation for OpenAPI 3.0:
openapi: 3.0.3
info:
title: Example API
version: 1.0.0
paths: {}
OpenAPI Object
Complete structure example:
openapi: 3.0.3
info:
title: Example API
description: Example REST API
version: 1.0.0
servers:
- url: https://api.example.com/v1
tags:
- name: users
description: User management
paths:
/users:
get:
tags:
- users
summary: Get users
operationId: listUsers
responses:
"200":
description: Successful response
components:
schemas: {}
security: []
Among them, openapi represents the OpenAPI specification version, and info.version represents the version of API itself.
3. Info Object
info defines the basic information of API.
info:
title: Example API
description: API documentation example
version: 1.0.0
termsOfService: https://example.com/terms
contact:
name: API Developer
url: https://example.com
email: developer@example.com
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0
| Field | function |
|---|---|
| title | API title |
| description | API description, supports Markdown |
| version | API version |
| termsOfService | Terms of Service |
| contact | Contact information |
| license | License information |
Where title and version are required fields.
4. Servers Object
servers describes the server address of API.
Multiple servers can be specified:
servers:
- url: https://dev.example.com/api/v1
description: Development
- url: https://staging.example.com/api/v1
description: Staging
- url: https://api.example.com/api/v1
description: Production
Use server variables
servers:
- url: https://{environment}.example.com/api/v{version}
variables:
environment:
default: api
enum:
- api
- dev
- staging
version:
default: "1"
enum:
- "1"
- "2"
variables allows the use of variables to define server addresses to avoid repeatedly writing similar configurations for different environments.
5. Paths Object and Operation Object
paths defines paths to API, each path containing one or more HTTP operations.
For example:
paths:
/users:
get:
summary: Get users
operationId: listUsers
responses:
"200":
description: Success
post:
summary: Create user
operationId: createUser
responses:
"201":
description: Created
/users/{userId}:
get:
summary: Get user
operationId: getUser
parameters:
- name: userId
in: path
required: true
schema:
type: integer
responses:
"200":
description: Success
Common fields of Operation Object
| Field | function |
|---|---|
| tags | Interface grouping |
| summary | Interface short description |
| description | Interface detailed description |
| operationId | Interface unique identifier |
| parameters | Path, Query, Header, Cookie parameters |
| requestBody | HTTP request body |
| responses | HTTP response definition |
| security | Override global authentication requirements |
| deprecated | Indicate whether an operation is deprecated |
| servers | Override server addresses |
| callbacks | Describe callbacks initiated by the API provider |
| externalDocs | external documentation |
where responses is a required field for Operation Object.
operationId
operationId: getUserProfile
operationId must remain unique throughout the OpenAPI document.
Code generation tools often use this to generate function names.
For example, Orval might be based on:
operationId: getUserProfile
Generate:
getUserProfile();
useGetUserProfile();
The specific generated results depend on the Orval configuration.
Therefore, it is recommended to use operationId, which is stable, clear and consistent with programming naming conventions.
6. Parameters Object
parameters is used to describe request parameters.
There are four positions:
| in | Parameter position | Example |
|---|---|---|
| path | URL path | /users/{userId} |
| query | query string | ?page=2 |
| header | HTTP Header | X-Request-ID |
| cookie | Cookie | sessionid |
Path Parameter
parameters:
- name: userId
in: path
required: true
description: User ID
schema:
type: integer
For in: path, required must be true.
Query Parameter
parameters:
- name: page
in: query
required: false
schema:
type: integer
minimum: 1
default: 1
- name: search
in: query
required: false
schema:
type: string
Corresponding request:
GET /users?page=2&search=Alice
Parameter Common fields
| Field | function |
|---|---|
| name | Parameter name |
| in | Parameter location |
| description | Parameter description |
| required | Is it required? |
| deprecated | Whether to deprecate |
| schema | Parameter data structure and verification constraints |
| example | Parameter example values |
| style | Parameter serialization method |
| explode | Control how array or object parameters are expanded |
| allowEmptyValue | Whether to allow null values, mainly for the Query parameter |
Serialization of array parameters
parameters:
- name: ids
in: query
style: form
explode: true
schema:
type: array
items:
type: integer
Corresponds to:
GET /users?ids=1&ids=2&ids=3
If set:
style: form
explode: false
Then it is usually serialized as:
GET /users?ids=1,2,3
7. Request Body Object
requestBody describes the data sent by the client.
Usually used for POST, PUT, PATCH and other requests.
requestBody:
required: true
description: User information
content:
application/json:
schema:
type: object
required:
- username
- email
properties:
username:
type: string
email:
type: string
format: email
password:
type: string
format: password
example:
username: johndoe
email: john@example.com
password: example-password
The difference between parameters and requestBody
| compare | parameters | requestBody |
|---|---|---|
| function | Describe independent request parameters | Describe the request body |
| location | path/query/header/cookie | HTTP Body |
| data structure | Usually defined for a single parameter Schema | Complete objects can be defined |
| Common uses | GET query, path parameters | POST, PUT, PATCH requests |
Note: HTTP does not absolutely prohibit GET requests from carrying Body, but GET Body does not have universally defined request semantics; OpenAPI 3.0 also does not support defining GET requestBody in a conventional way.
Multiple media types
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/User"
application/x-www-form-urlencoded:
schema:
$ref: "#/components/schemas/User"
content can define multiple media types, including JSON, form data and file upload.
8. Responses Object
responses describes the response of API under different HTTP status codes.
responses:
"200":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"400":
description: Invalid request
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"404":
description: User not found
"500":
description: Internal server error
Common status codes:
| status code | meaning |
|---|---|
| 200 | Request successful |
| 201 | Resource created successfully |
| 202 | Request accepted, may be processed asynchronously |
| 204 | Request successful, no response body |
| 400 | Request parameter error |
| 401 | Missing valid authentication credentials |
| 403 | Forbidden access |
| 404 | Resource does not exist |
| 409 | Resource status conflict |
| 422 | The request content cannot be processed |
| 500 | Server internal error |
unresponsive body
For 204 No Content:
responses:
"204":
description: Successfully deleted
There is no need to define content.
Default response
responses:
"200":
description: Success
default:
description: Unexpected error
default can describe other response situations not listed separately.
9. Components Object
components is the reusable component container of OpenAPI.
Mainly includes:
| Field | function |
|---|---|
| schemas | Reusable data model |
| parameters | Reusable request parameters |
| responses | Reusable responses |
| requestBodies | Reusable request body |
| headers | Reusable response header definitions |
| examples | Reusable examples |
| securitySchemes | Authentication mechanism definition |
| links | API Relationship between operations |
| callbacks | Reusable callbacks |
Definition Schema
components:
schemas:
User:
type: object
required:
- id
- username
properties:
id:
type: integer
username:
type: string
email:
type: string
format: email
Define reusable parameters
components:
parameters:
PageParam:
name: page
in: query
schema:
type: integer
minimum: 1
default: 1
Define reusable responses
components:
responses:
NotFound:
description: Resource not found
content:
application/json:
schema:
type: object
properties:
detail:
type: string
10. $ref: component reference
$ref is used to reference already defined components to reduce duplicate code.
For example:
paths:
/users:
get:
operationId: listUsers
parameters:
- $ref: "#/components/parameters/PageParam"
responses:
"404":
$ref: "#/components/responses/NotFound"
Schema Quote:
schema:
$ref: "#/components/schemas/User"
Reference path:
#/components/schemas/User
Meaning is quoted from the current document:
components
└── schemas
└── User
$ref also supports referencing definitions in external files or URL.
In OpenAPI 3.0, other sibling fields of Reference Object are usually ignored, so it is not recommended to override the original definition by adding fields such as description directly next to $ref.
11. Schema Object: Data model
Schema Object is used to describe data types, fields and validation rules.
OpenAPI 3.0's Schema is based on an extended subset of JSON Schema, rather than fully supporting all JSON Schema keywords.
Common data types
| Type | meaning | Commonly used format |
|---|---|---|
| string | string | date、date-time、email、uuid、binary |
| integer | integer | int32、int64 |
| number | numerical value | float、double |
| boolean | Boolean value | — |
| array | array | — |
| object | object | — |
format is a further description of the basic type, and the specific verification behavior depends on the tool used.
Complete Schema example
components:
schemas:
Product:
type: object
required:
- id
- name
- price
properties:
id:
type: integer
format: int64
readOnly: true
name:
type: string
minLength: 1
maxLength: 100
description:
type: string
nullable: true
price:
type: number
minimum: 0
category:
type: string
enum:
- electronics
- books
- clothing
tags:
type: array
items:
type: string
metadata:
type: object
additionalProperties: true
Common validation fields
| Field | Description |
|---|---|
| required | Required attribute list |
| nullable | Is null (OpenAPI 3.0) allowed? |
| default | Default value description |
| enum | Limit allowed values |
| minimum / maximum | Numeric range |
| minLength / maxLength | String length limit |
| pattern | Regular expression constraints |
| minItems / maxItems | Limit on the number of array elements |
| uniqueItems | Are array elements unique? |
| additionalProperties | Whether to allow additional object properties |
| readOnly | read-only fields |
| writeOnly | write-only fields |
| deprecated | Tag field is deprecated |
The difference between required and nullable
type: object
required:
- name
properties:
name:
type: string
nullable: true
required: This field must exist in the object.nullable: Allowed field value isnull.
Therefore, this example requires that name be provided, but allows the value null.
Fields not written in the required list mean they can be omitted and do not automatically mean null is allowed.
12. allOf / oneOf / anyOf
OpenAPI supports building complex data structures using combining keywords.
| Keywords | meaning |
|---|---|
| allOf | Must satisfy all Schema |
| oneOf | Must satisfy exactly one Schema |
| anyOf | Meet at least one Schema |
| not | Does not meet specified Schema |
allOf: Combination Schema
components:
schemas:
Pet:
type: object
required:
- name
properties:
name:
type: string
Dog:
allOf:
- $ref: "#/components/schemas/Pet"
- type: object
properties:
breed:
type: string
Dog satisfies both Pet and the new field constraints.
allOf is often used to compose data models, but does not imply object-oriented inheritance at the programming language level.
oneOf: Multiple possible data models
schema:
oneOf:
- $ref: "#/components/schemas/Dog"
- $ref: "#/components/schemas/Cat"
Indicates that the data must match exactly one of Schema.
If you need to distinguish between different object types, you can use it in combination with discriminator.
13. Security Schemes
OpenAPI uses components.securitySchemes to define the authentication mechanism, and then uses security to declare the authentication method required by the interface.
Bearer Token (commonly used in JWT)
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
security:
- BearerAuth: []
Corresponding to HTTP request:
Authorization: Bearer <token>
bearerFormat: JWT is primarily a documentation hint and does not mean that OpenAPI automatically validates JWT.
API Key
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
Session Cookie
components:
securitySchemes:
SessionAuth:
type: apiKey
in: cookie
name: sessionid
This way describes the credentials passed via Cookie, but the actual Session validation is still done by the backend.
CSRF Token also does not gain protection capabilities simply by defining securitySchemes.
Override authentication for a single interface
paths:
/public:
get:
security: []
responses:
"200":
description: Success
security: [] indicates that the global security requirements inherited by this operation are removed.
14. Tags and ExternalDocs
Tags: Interface grouping
tags:
- name: users
description: User management
- name: products
description: Product management
paths:
/users:
get:
tags:
- users
responses:
"200":
description: Success
Tags is mainly used for interface classification of document tools such as Swagger UI.
ExternalDocs: external document
externalDocs:
description: Additional API documentation
url: https://example.com/docs
External documentation information can be provided at the top level, in operations, or in partial Schema definitions.
15. File upload: multipart/form-data
File uploads usually use multipart/form-data.
paths:
/users/avatar:
post:
summary: Upload avatar
operationId: uploadAvatar
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required:
- file
properties:
file:
type: string
format: binary
responses:
"200":
description: Upload successful
Key points:
type: string
format: binary
Represents the binary file field.
For Django REST Framework, this can usually be combined with:
from rest_framework.parsers import MultiPartParser
Handle upload requests.
16. drf-spectacular and Django REST Framework
drf-spectacular can automatically generate OpenAPI Schema based on the View, Serializer, URL and other information of DRF.
Basic configuration
# settings.py
INSTALLED_APPS = [
"rest_framework",
"drf_spectacular",
]
REST_FRAMEWORK = {
"DEFAULT_SCHEMA_CLASS":
"drf_spectacular.openapi.AutoSchema",
}
SPECTACULAR_SETTINGS = {
"TITLE": "Example API",
"DESCRIPTION": "REST API documentation",
"VERSION": "1.0.0",
}
Configure API document routing
# urls.py
from django.urls import path
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularSwaggerView,
SpectacularRedocView,
)
urlpatterns = [
path(
"api/schema/",
SpectacularAPIView.as_view(),
name="schema",
),
path(
"api/docs/",
SpectacularSwaggerView.as_view(
url_name="schema"
),
name="swagger-ui",
),
path(
"api/redoc/",
SpectacularRedocView.as_view(
url_name="schema"
),
name="redoc",
),
]
These routes are used for:
/api/schema/: Get OpenAPI Schema./api/docs/: View Swagger UI./api/redoc/: View ReDoc documentation.
extend_schema
@extend_schema is used to supplement or overwrite the automatically generated OpenAPI information.
from drf_spectacular.utils import (
extend_schema,
OpenApiParameter,
OpenApiTypes,
)
from rest_framework.views import APIView
from rest_framework.response import Response
class UserView(APIView):
@extend_schema(
tags=["Users"],
summary="Get user",
operation_id="getUser",
parameters=[
OpenApiParameter(
name="user_id",
type=OpenApiTypes.INT,
location=OpenApiParameter.QUERY,
required=True,
)
],
responses={200: UserSerializer},
)
def get(self, request):
...
Parameters, request body and response body
In extend_schema:
| parameters | Corresponds to OpenAPI |
|---|---|
parameters | Parameter Object |
request | Request Body Object |
responses | Responses Object |
operation_id | operationId |
summary | summary |
description | description |
tags | tags |
deprecated | deprecated |
For example:
@extend_schema(
operation_id="createUser",
request=UserCreateSerializer,
responses={
201: UserResponseSerializer,
400: ErrorSerializer,
},
)
def post(self, request):
...
extend_schema_view
For cases where Schema needs to be specified for multiple methods at the class level:
from drf_spectacular.utils import (
extend_schema,
extend_schema_view,
)
@extend_schema_view(
get=extend_schema(
operation_id="listUsers",
responses=UserSerializer(many=True),
),
post=extend_schema(
operation_id="createUser",
request=UserCreateSerializer,
responses={201: UserSerializer},
),
)
class UserView(APIView):
...
extend_schema_view is suitable for unified description of multiple operations in a View.
Generate OpenAPI file
python manage.py spectacular \
--file schema.yaml \
--validate
The generated schema.yaml can be used for:
- Swagger UI
- ReDoc
- Orval
- OpenAPI Generator
- API Schema check
It should be noted that the successful verification of Schema can only mean that the document basically complies with the specifications, and it cannot guarantee that the actual behavior of API is completely consistent with the document.
17. OpenAPI and front-end code generation
For React + TypeScript projects, you can use Orval to automatically generate API Client and type definitions based on OpenAPI Schema.
Django Serializer
↓
drf-spectacular
↓
OpenAPI Schema
↓
Orval
↓
TypeScript Types
Axios Functions
TanStack Query Hooks
For example backend:
@extend_schema(
operation_id="getUser",
responses=UserSerializer,
)
The front end may generate:
getUser();
useGetUser();
The main value of this approach is:
- Reduce the repeated definition of interface types at the front and back ends.
- Improve request and response type safety.
- Simplify maintenance of API Client.
- Reduce the risk of inconsistent field naming.
But please note: OpenAPI Schema is just an interface contract, and the generated TypeScript type does not guarantee that the server will return data that conforms to Schema when it is running.
Therefore, data validation, testing, and Schema accuracy on the backend remain important.
18. The difference between OpenAPI 3.0 and 3.1
OpenAPI 3.1 makes important adjustments to Schema support to make it more consistent with JSON Schema Draft 2020-12.
| Features | OpenAPI 3.0 | OpenAPI 3.1 |
|---|---|---|
| Schema Basics | JSON Schema extended subset | Alignment JSON Schema 2020-12 |
| nullable | Use nullable: true | Usually a union type is used to represent null |
| JSON Schema keyword | Limited support | Support more complete |
| Schema Compatibility | Need to pay attention to the unique constraints of OpenAPI | Closer to standard JSON Schema |
OpenAPI 3.0:
type: string
nullable: true
OpenAPI 3.1:
type:
- string
- "null"
In addition, in OpenAPI 3.1, related mechanisms such as $ref have also been further improved.
In actual development, the OpenAPI version should be selected based on the compatibility of the documentation tool and code generator used, and it is not necessarily necessary to upgrade to the latest version immediately.
Summary
The core content of OpenAPI can be divided into four parts:
| Classification | focus |
|---|---|
| Document structure | OpenAPI、Info、Servers、Paths |
| Interface definition | Operation、Parameters、RequestBody、Responses |
| data model | Schema、Components、$ref、allOf、oneOf |
| engineering practice | SecuritySchemes、Swagger UI、drf-spectacular、Orval |
What needs to be mastered most is:
pathsand Operation Object: Describe the interface.parametersandrequestBody: Description input.responsesandschemas: Describe the output and data structure.componentsand$ref: Implement component reuse.operationId: Support stable client code generation.securitySchemes: Describes interface certification requirements.
For the development of Django + DRF, it is recommended to use Serializer as the main data structure definition, then automatically generate OpenAPI Schema through drf-spectacular, and finally give it to Swagger UI and Orval for use.