The OpenAPI Root Object
The OpenAPI root object is the top-level container for the entire API specification. In this codebase, it is represented by the OpenAPI class, which serves as the entry point for all metadata, path definitions, and reusable components that describe the API.
The OpenAPI Model Structure
The OpenAPI class, defined in fastapi/openapi/models.py, is a Pydantic model that structures the final JSON schema. It inherits from BaseModelWithConfig, which is configured with extra: "allow". This allows the inclusion of specification extensions (fields prefixed with x-) that are not explicitly defined in the OpenAPI standard but are supported by various tools.
class OpenAPI(BaseModelWithConfig):
openapi: str
info: Info
jsonSchemaDialect: str | None = None
servers: list[Server] | None = None
paths: dict[str, PathItem | Any] | None = None
webhooks: dict[str, PathItem | Reference] | None = None
components: Components | None = None
security: list[dict[str, list[str]]] | None = None
tags: list[Tag] | None = None
externalDocs: ExternalDocumentation | None = None
The root object organizes information into several key sections:
- Metadata: Handled by the
infofield. - Connectivity: Defined in the
serverslist. - Endpoints: Contained within
pathsandwebhooks. - Reusability: Managed through the
componentsobject.
API Metadata with the Info Object
The Info class provides essential metadata about the API. When you instantiate a FastAPI application, parameters like title, version, and description are mapped directly to this model.
class Info(BaseModelWithConfig):
title: str
summary: str | None = None
description: str | None = None
termsOfService: str | None = None
contact: Contact | None = None
license: License | None = None
version: str
In fastapi/openapi/utils.py, the get_openapi function extracts these values from the application instance to populate the Info object:
# From fastapi/openapi/utils.py
info: dict[str, Any] = {"title": title, "version": version}
if summary:
info["summary"] = summary
if description:
info["description"] = description
# ...
Defining Target Environments with Servers
The Server class represents a target environment where the API is hosted. This allows the documentation to provide functional "Try it out" features against different URLs (e.g., production, staging, or local).
class Server(BaseModelWithConfig):
url: AnyUrl | str
description: str | None = None
variables: dict[str, ServerVariable] | None = None
You can configure these servers directly in the FastAPI constructor, as demonstrated in tests/test_openapi_servers.py:
app = FastAPI(
servers=[
{"url": "/", "description": "Default, relative server"},
{"url": "https://prod.example.com"},
]
)
Reusable Definitions in Components
The Components class acts as a registry for objects that are referenced multiple times throughout the API definition. This reduces redundancy and ensures consistency across the schema.
class Components(BaseModelWithConfig):
schemas: dict[str, Schema | Reference] | None = None
responses: dict[str, Response | Reference] | None = None
parameters: dict[str, Parameter | Reference] | None = None
securitySchemes: dict[str, SecurityScheme | Reference] | None = None
# ...
FastAPI automatically populates components["schemas"] by analyzing the Pydantic models used in your route signatures. It also populates securitySchemes based on the security dependencies (like OAuth2PasswordBearer or HTTPBearer) attached to your routes.
Schema Generation and Customization
The generation of the OpenAPI root object is handled by the get_openapi utility function. This function iterates through all registered routes, extracts their parameters and response models, and assembles them into the OpenAPI model.
Lazy Loading and Caching
The FastAPI application class manages the OpenAPI schema lazily. The schema is not generated until the openapi() method is called (typically when a user hits the /openapi.json endpoint). Once generated, it is cached in app.openapi_schema to avoid the overhead of re-calculation.
# From fastapi/applications.py
def openapi(self) -> dict[str, Any]:
if not self.openapi_schema:
self.openapi_schema = get_openapi(
title=self.title,
version=self.version,
# ... other parameters ...
routes=self.routes,
)
return self.openapi_schema
Advanced Customization
For advanced use cases, such as adding custom security schemes or modifying the generated schemas, you can override the app.openapi method or modify the app.openapi_schema attribute directly after the first generation. Because the OpenAPI model and its children allow extra fields, you can safely inject custom metadata or extensions that will persist through the serialization process.