Skip to main content

OpenAPI and Metadata

In this project, OpenAPI is not merely a documentation artifact but the central contract that defines the API's behavior, structure, and metadata. The implementation leverages the OpenAPI 3.1.0 specification to provide a machine-readable schema that powers interactive documentation interfaces like Swagger UI and ReDoc, as well as client-side code generation.

Centralized Metadata Configuration​

The primary mechanism for configuring API metadata is the FastAPI class constructor in fastapi/applications.py. This design choice centralizes the "Info Object" of the OpenAPI specification, allowing developers to define the identity and legal context of the API in a single location.

Key parameters in the FastAPI constructor include:

  • title: The name of the API (defaults to "FastAPI").
  • summary: A short summary of the API.
  • description: A detailed description, which supports Markdown.
  • version: The version of the application (defaults to "0.1.0").
  • contact and license_info: Dictionaries containing contact details and licensing terms.

These parameters are passed to the get_openapi utility function in fastapi/openapi/utils.py, which constructs the final JSON schema. For example, the license_info parameter is mapped to the license field in the OpenAPI info block, supporting both URLs and SPDX identifiers.

Organizing Documentation with Tags​

To prevent large APIs from becoming a monolithic list of endpoints, the project uses a tagging system. While tags can be applied directly to path operations (e.g., @app.get("/users/", tags=["users"])), the openapi_tags parameter in the FastAPI constructor allows for rich metadata and ordering of these tags.

As seen in docs_src/metadata/tutorial004_py310.py, this metadata can include descriptions and external documentation links:

from fastapi import FastAPI

tags_metadata = [
{
"name": "users",
"description": "Operations with users. The **login** logic is also here.",
},
{
"name": "items",
"description": "Manage items. So _fancy_ they have their own docs.",
"externalDocs": {
"description": "Items external docs",
"url": "https://fastapi.tiangolo.com/",
},
},
]

app = FastAPI(openapi_tags=tags_metadata)

@app.get("/users/", tags=["users"])
async def get_users():
return [{"name": "Harry"}]

This approach decouples the documentation of a category (the tag) from the individual routes that belong to it, ensuring that the UI remains organized and informative.

Environment and Server Configuration​

The servers parameter allows the API to declare multiple base URLs, which is critical for APIs deployed across different environments (e.g., staging, production).

In fastapi/applications.py, the FastAPI class also manages the root_path. When an application is behind a proxy, the root_path ensures that the generated OpenAPI schema and the interactive docs use the correct base URL. By default, root_path_in_servers is True, meaning FastAPI will automatically prepend the root_path to the servers list if it is not already present.

The OpenAPI Lifecycle and Caching​

The generation of the OpenAPI schema is managed by the openapi() method of the FastAPI instance. To optimize performance, the schema is generated lazily and cached in the app.openapi_schema attribute.

# 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 metadata ...
routes=self.routes,
tags=self.openapi_tags,
servers=self.servers,
)
return self.openapi_schema

This caching mechanism means that any modifications to the application (like adding routes or changing metadata) after the first call to the /openapi.json endpoint will not be reflected in the schema unless app.openapi_schema is manually cleared.

Customizing the Schema​

For advanced use cases where the standard parameters are insufficient—such as adding custom OpenAPI extensions (fields starting with x-)—the project supports overriding the app.openapi method.

A common pattern, demonstrated in docs_src/extending_openapi/tutorial001_py310.py, involves calling the original get_openapi utility and then modifying the resulting dictionary before caching it:

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",
summary="This is a very custom OpenAPI schema",
description="Description with **Markdown**",
routes=app.routes,
)
# Adding 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

Security and Production Constraints​

In production environments, it is often desirable to hide the API documentation. This is achieved by setting openapi_url=None in the FastAPI constructor. Because the interactive documentation tools (/docs and /redoc) depend on the OpenAPI JSON, disabling the openapi_url automatically disables these endpoints as well. This is a safety feature implemented in the setup() method of fastapi/applications.py, ensuring that no metadata is leaked if the schema endpoint is suppressed.