fastapi/fastapi
OpenAPI generation
End-to-end picture of how a FastAPI application produces an OpenAPI 3.1 document. For the per-module reference of the schema models and HTML helpers, see systems/openapi.
Where the data comes from
OpenAPI generation re-uses information FastAPI has already collected for the dependency-injection system. There is no second pass over user code:
graph TD
UserCode["@app.get('/items/{id}')<br/>def read_item(id: int): ..."]
UserCode -->|registration| AddRoute["APIRouter.add_api_route"]
AddRoute -->|build once| Dep["Dependant<br/>fastapi/dependencies/models.py"]
Dep --> APIRoute["APIRoute (stored on the router)"]
Request --> AppOpenAPI["app.openapi()"]
AppOpenAPI -->|delegates to| GetOA["get_openapi(...)<br/>fastapi/openapi/utils.py"]
GetOA --> Walk["Walk every APIRoute"]
Walk --> Project["Project Dependant -> OpenAPI fragments"]
Project --> Doc["OpenAPI document (dict)"]
Doc --> Cache["app.openapi_schema (cached)"]
Cache --> JSON["/openapi.json"]
Cache --> Swagger["/docs (Swagger UI)"]
Cache --> ReDoc["/redoc"]Inputs
get_openapi(...) (fastapi/openapi/utils.py) takes:
title,version,summary,description,terms_of_service,contact,license_info→inforoutes: list[BaseRoute]→pathswebhooks: list[BaseRoute] | None→webhookstags: list[dict] | None→tagsservers: list[dict] | None→serversopenapi_version→openapiseparate_input_output_schemas: bool→ toggles separate request/response schemas (default True)
FastAPI.openapi() reads each of these from the FastAPI instance and the active route table. The result is cached on app.openapi_schema. Mutating the dict and re-serving from the cache is the supported customisation pattern.
Per-route projection
For each route the generator builds an Operation:
| OpenAPI field | Source |
|---|---|
operationId |
route.unique_id (set during add_api_route from generate_unique_id_function, default in fastapi/utils.py:generate_unique_id). |
summary, description, tags, deprecated |
Set on the route at registration. |
parameters |
_get_openapi_operation_parameters(...) walks the flat Dependant's path/query/header/cookie params and converts each ModelField to a JSON Schema fragment via _compat.get_schema_from_model_field. |
requestBody |
_get_openapi_operation_request_body(...) from body_params, switching media type for Form and File. |
responses |
Combines route.responses (user-supplied), route.response_model, route.status_code, and the validation-error response. |
security |
get_openapi_security_definitions(...) walks the flat dependant for SecurityBase sub-dependants and emits [{scheme: scopes}]. |
The header parameter logic includes a special case: when convert_underscores=True (the default for Header), user_agent is rewritten as User-Agent in the OpenAPI schema. That mirrors the runtime behaviour.
Schema collection
OpenAPI requires JSON Schemas for every model used in the operations. get_openapi:
- Calls
get_flat_models_from_fields(fastapi/_compat) on everyModelFieldinvolved. - Hands the set to
get_definitionsfrom Pydantic v2 (via_compat.get_definitions) which returns(field_mapping, definitions). - Stores
definitionsundercomponents.schemas. - Always injects
ValidationErrorandHTTPValidationError(fromvalidation_error_definitionandvalidation_error_response_definitioninopenapi/utils.py).
Models can appear under separate keys for input and output when separate_input_output_schemas=True (Pydantic v2 generates distinct schemas if a model uses computed fields or read-only/write-only marks).
Server-Sent Events
For routes that set response_class=EventSourceResponse, the response section is set to text/event-stream with the _SSE_EVENT_SCHEMA fragment from fastapi/sse.py. That schema follows the OpenAPI 3.2 SSE addendum ({data, event, id, retry}). See Server-Sent Events.
Webhooks
webhooks: list[BaseRoute] follows the same projection pipeline as routes. Webhook routes never serve traffic — they exist only to describe outbound calls in OpenAPI.
Customising
| Goal | How |
|---|---|
| Different operation IDs | Pass generate_unique_id_function on FastAPI() (or per APIRouter). The function receives an APIRoute and returns a string. |
Hide a route from /docs |
Set include_in_schema=False on the decorator. |
| Hide a parameter | Set include_in_schema=False on the parameter (Param.include_in_schema). |
| Add servers | FastAPI(servers=[{"url": "..."}]). |
Add an x- extension |
Mutate app.openapi_schema after first generation. |
| Translate descriptions | Use get_openapi(...) directly with localised strings. |
Integration points
- From
FastAPI—FastAPI.openapi()is the entry. Caching lives there. - From routing — every
APIRoutealready has itsDependantand stored metadata. - From
_compat— schema generation is delegated to Pydantic via_compat.get_definitions. - To Swagger UI / ReDoc — both UIs fetch
/openapi.json(configurable). The HTML for them lives infastapi/openapi/docs.py.
Entry points for modification
fastapi/openapi/utils.py:get_openapi— top-level orchestrator.fastapi/openapi/models.py— Pydantic models. Add fields withextra = "allow"already configured for spec extensions.fastapi/openapi/docs.py— change theme, asset URLs, OAuth2 init plumbing.fastapi/utils.py:generate_unique_id— change default operation IDs (rare; usually overridden per-app instead).
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.
Previous
Dependency injection
Next
Server-Sent Events