Skip to main content

OpenAPI Schema Generation and Customization

FastAPI leverages the OpenAPI specification (defaulting to version 3.1.0) to provide automatic, interactive documentation. This process is deeply integrated into the application's lifecycle, transforming Python type hints and route definitions into a structured JSON schema.

The Generation Lifecycle

The generation of the OpenAPI schema is managed by the FastAPI class in fastapi/applications.py. The primary entry point is the openapi() method, which acts as a coordinator and cache manager.

When a client (like Swagger UI at /docs) requests the schema, FastAPI.openapi() is called. Internally, it delegates the heavy lifting to the get_openapi utility function found in fastapi/openapi/utils.py.

def openapi(self) -> dict[str, Any]:
if not self.openapi_schema:
self.openapi_schema = get_openapi(
title=self.title,
version=self.version,
openapi_version=self.openapi_version,
summary=self.summary,
description=self.description,
routes=self.routes,
webhooks=self.webhooks.routes,
tags=self.openapi_tags,
servers=self.servers,
# ... other metadata ...
)
return self.openapi_schema

Caching and Performance

FastAPI implements a simple but effective caching strategy. The generated schema is stored in the self.openapi_schema attribute. Subsequent calls to the openapi() method return the cached dictionary instead of re-calculating it. This is a critical design choice because generating the schema involves inspecting every route, Pydantic model, and dependency in the application, which can be computationally expensive for large APIs.

Customizing Metadata

The FastAPI constructor provides numerous parameters to enrich the generated schema's info object and top-level configuration.

  • Basic Info: title, summary, description, and version populate the standard OpenAPI info fields.
  • Contact and License: The contact and license_info dictionaries allow for detailed legal and support metadata.
  • Server Configuration: The servers parameter accepts a list of dictionaries (e.g., {"url": "https://prod.example.com", "description": "Production"}) to define the base URLs for the API.
  • Tags: The openapi_tags parameter allows you to provide metadata for tags used in path operations, including descriptions and links to external documentation.
app = FastAPI(
title="ChimichangApp",
openapi_tags=[
{
"name": "users",
"description": "Operations with users.",
}
]
)

Manual Schema Overrides

For advanced use cases where the default generation logic is insufficient—such as adding custom vendor extensions (e.g., x-logo) or modifying the security requirements globally—FastAPI allows you to override the app.openapi method entirely.

The standard pattern involves defining a custom function that calls get_openapi, modifies the resulting dictionary, and assigns it back to the app instance.

from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi

app = FastAPI()

def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="Custom title",
version="2.5.0",
routes=app.routes,
)
# Inject a custom extension
openapi_schema["info"]["x-logo"] = {
"url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
}
app.openapi_schema = openapi_schema
return app.openapi_schema

app.openapi = custom_openapi

Implementing Webhooks

Webhooks allow you to document APIs that your service calls rather than just the ones it receives. In fastapi/applications.py, the FastAPI class initializes a webhooks attribute as an APIRouter.

Unlike standard path operations, webhooks do not create actual endpoints on your server. Instead, they are used exclusively for documentation in the OpenAPI schema.

app = FastAPI()

@app.webhooks.post("new-subscription")
def new_subscription(body: Subscription):
"""
When a new user subscribes, we will send a POST request to your registered URL.
"""

In the get_openapi utility, these are processed similarly to routes but are placed in the webhooks section of the final OpenAPI JSON.

Advanced Route Customization

Individual path operations can be customized or excluded from the schema using parameters in the decorator:

  • include_in_schema: Setting this to False (e.g., @app.get("/internal", include_in_schema=False)) prevents the route from appearing in /docs or the JSON schema.
  • openapi_extra: This parameter accepts a dictionary that is deeply merged into the generated OpenAPI operation object. This is the primary way to add specific OpenAPI features not directly supported by FastAPI's decorator arguments.
  • operation_id: While FastAPI generates these automatically (using the function name by default), you can provide a custom string to ensure stable IDs for client generators.
@app.get("/items/", openapi_extra={"x-custom-metadata": "value"})
async def read_items():
return [{"name": "Foo"}]

This granular control ensures that while the "magic" of automatic generation handles 90% of use cases, the remaining 10% can be achieved through explicit configuration and overrides.