Skip to main content

Reusable Components and References

To define reusable elements like schemas, request bodies, or security schemes in your OpenAPI document, you can leverage the Components and Reference models. This allows you to define a component once and reference it across multiple path operations.

Defining and Referencing Reusable Components

The following example demonstrates how to define a reusable RequestBody in the components section and reference it in a path operation using openapi_extra.

from fastapi import FastAPI, Request
from fastapi.openapi.utils import get_openapi
from fastapi.openapi.models import OpenAPI, Components, RequestBody, MediaType, Reference
from fastapi.encoders import jsonable_encoder

app = FastAPI()

# Reference the component in the route using the $ref key
@app.post(
"/items/",
openapi_extra={
"requestBody": {"$ref": "#/components/requestBodies/SharedItemBody"}
}
)
async def create_item(request: Request):
return {"message": "Item created"}

def custom_openapi():
if app.openapi_schema:
return app.openapi_schema

# Generate the base OpenAPI schema
openapi_schema = get_openapi(
title="Custom Components API",
version="1.0.0",
routes=app.routes,
)

# Define the reusable RequestBody component
shared_body = RequestBody(
content={
"application/json": MediaType(
schema={
"type": "object",
"properties": {
"name": {"type": "string"},
"price": {"type": "number"}
},
"required": ["name", "price"]
}
)
},
description="A shared request body for item creation"
)

# Initialize components if not present
if "components" not in openapi_schema:
openapi_schema["components"] = {}

# Add the component to the schema
# We use jsonable_encoder to ensure models are converted to dicts with correct aliases
components_dict = openapi_schema["components"]
components_dict.setdefault("requestBodies", {})["SharedItemBody"] = jsonable_encoder(
shared_body, exclude_none=True, by_alias=True
)

app.openapi_schema = openapi_schema
return app.openapi_schema

app.openapi = custom_openapi

Using the Reference Class

While you can use raw dictionaries for references (e.g., {"$ref": "..."}), the Reference class provides a structured way to define JSON references. It uses a Pydantic alias to map the ref field to $ref.

from fastapi.openapi.models import Reference
from fastapi.encoders import jsonable_encoder

# Define a reference to a schema
ref = Reference(ref="#/components/schemas/Item")

# Serialization results in the correct OpenAPI key
# {"$ref": "#/components/schemas/Item"}
serialized_ref = jsonable_encoder(ref, by_alias=True)

Extending Components with Specification Extensions

Most OpenAPI models in this project, including Components and RequestBody, inherit from BaseModelWithConfig. This base class is configured with extra: "allow", enabling you to add custom "Specification Extensions" (fields starting with x-) directly to the objects.

from fastapi.openapi.models import Components, Info

# Adding a custom extension to the Info object
info = Info(
title="Extended API",
version="1.0.0",
description="An API with custom metadata"
)
# BaseModelWithConfig allows extra fields
info.x_logo = {"url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"}

# Adding extensions to Components
components = Components(schemas={})
components.x_generator = "FastAPI Custom Script"

Troubleshooting

Correct Alias Handling

The Reference class and many other OpenAPI models use aliases (like $ref for ref or in for in_). When manually manipulating the openapi_schema dictionary, always use jsonable_encoder(model, by_alias=True) to ensure the keys match the OpenAPI specification.

Reference Prefixes

When creating manual references, ensure you use the correct prefix for the component type:

  • Schemas: #/components/schemas/ (Available as REF_PREFIX in fastapi.openapi.constants)
  • Responses: #/components/responses/
  • Parameters: #/components/parameters/
  • Request Bodies: #/components/requestBodies/
  • Security Schemes: #/components/securitySchemes/

Schema Caching

When overriding app.openapi, always check if app.openapi_schema is already set. This prevents unnecessary re-generation of the schema on every request to the /openapi.json endpoint.

def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
# ... generation logic ...
app.openapi_schema = openapi_schema
return app.openapi_schema