Factory.ai

Open-Source Wikis

/

FastAPI

/

Features

/

OpenAPI generation

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_infoinfo
  • routes: list[BaseRoute]paths
  • webhooks: list[BaseRoute] | Nonewebhooks
  • tags: list[dict] | Nonetags
  • servers: list[dict] | Noneservers
  • openapi_versionopenapi
  • separate_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:

  1. Calls get_flat_models_from_fields (fastapi/_compat) on every ModelField involved.
  2. Hands the set to get_definitions from Pydantic v2 (via _compat.get_definitions) which returns (field_mapping, definitions).
  3. Stores definitions under components.schemas.
  4. Always injects ValidationError and HTTPValidationError (from validation_error_definition and validation_error_response_definition in openapi/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 FastAPIFastAPI.openapi() is the entry. Caching lives there.
  • From routing — every APIRoute already has its Dependant and 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 in fastapi/openapi/docs.py.

Entry points for modification

  • fastapi/openapi/utils.py:get_openapi — top-level orchestrator.
  • fastapi/openapi/models.py — Pydantic models. Add fields with extra = "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.

OpenAPI generation – FastAPI wiki | Factory