Skip to main content

Components and Reusable Objects

When you define complex data structures using Pydantic models, FastAPI ensures your OpenAPI documentation remains clean and DRY (Don't Repeat Yourself) by utilizing the components section. Instead of redefining the same model every time it appears in a request or response, FastAPI defines it once in components/schemas and references it elsewhere using the $ref field.

The Components Registry

The Components class in fastapi.openapi.models serves as the central registry for all reusable objects in your API definition. It includes fields for various OpenAPI components:

  • schemas: Data models (usually derived from Pydantic).
  • responses: Reusable HTTP responses.
  • parameters: Reusable path, query, header, or cookie parameters.
  • examples: Reusable example values.
  • securitySchemes: Authentication and authorization schemes (e.g., OAuth2, API Keys).
  • links: Relationships between different operations.
  • requestBodies: Reusable request body definitions.

FastAPI automatically populates these fields during the OpenAPI generation process in fastapi.openapi.utils.get_openapi. This function assembles the final OpenAPI model, which is the root object of your API's schema.

Automatic Schema Generation

When you use a Pydantic model as a type hint for a request body or a response_model, FastAPI extracts the JSON Schema for that model and places it in the components.schemas dictionary.

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
name: str
price: float

app = FastAPI()

@app.post("/items/", response_model=Item)
async def create_item(item: Item):
return item

Internally, get_openapi calls get_definitions (from fastapi._compat) to collect all models used in your routes. These are then assigned to components["schemas"] in fastapi/openapi/utils.py:

# Logic from fastapi/openapi/utils.py
if definitions:
components["schemas"] = {k: definitions[k] for k in sorted(definitions)}

Referencing Objects with Reference

To point to a reusable component, FastAPI uses the Reference class, which implements the $ref field. By default, FastAPI uses the REF_PREFIX (#/components/schemas/) defined in fastapi.openapi.constants.

For example, when FastAPI automatically generates a 422 Validation Error response, it uses a reference to the HTTPValidationError schema:

# Implementation in fastapi/openapi/utils.py
operation["responses"]["422"] = {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/HTTPValidationError"}
}
},
}

FastAPI provides models for adding descriptive metadata like Example and Link to your components.

Examples

The Example class is a TypedDict in fastapi.openapi.models that allows you to provide sample data for parameters or request bodies. You can use it within Body, Query, or Path parameters via the openapi_examples argument.

from fastapi import Body, FastAPI
from fastapi.openapi.models import Example

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: dict = Body(
openapi_examples={
"normal": Example(
summary="A normal item",
description="A standard item example",
value={"name": "Foo", "price": 42.0},
),
}
),
):
return item

The Link class allows you to describe how the output of one operation can be used as the input for another. While FastAPI doesn't generate these automatically, you can add them to your responses.

from fastapi import FastAPI
from fastapi.openapi.models import Link

app = FastAPI()

@app.get("/users/{user_id}", responses={
200: {
"links": {
"GetUserOrders": Link(
operationId="get_orders",
parameters={"user_id": "$response.body#/id"}
)
}
}
})
async def get_user(user_id: str):
return {"id": user_id, "name": "John Doe"}

Advanced Customization

If you need to manually inject components or modify the generated schema, FastAPI provides two primary mechanisms.

Route-level: openapi_extra

Pass a dictionary to the openapi_extra parameter of any path operation decorator. This dictionary is deeply merged into the generated Operation object, allowing you to reference components that FastAPI might not detect automatically.

@app.post(
"/items/",
openapi_extra={
"requestBody": {
"content": {"application/x-yaml": {"schema": Item.model_json_schema()}},
"required": True,
},
},
)
async def create_item(request: Request):
...

Global-level: Overriding app.openapi()

For global changes, such as adding a custom security scheme or a shared response to the Components object, override the app.openapi method.

from fastapi.openapi.utils import get_openapi

def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="Custom API",
version="1.0.0",
routes=app.routes,
)

# Ensure the components and responses dictionaries exist
components = openapi_schema.setdefault("components", {})
responses = components.setdefault("responses", {})

# Manually add a reusable response
responses["NotFound"] = {
"description": "The resource was not found",
"content": {"application/json": {"schema": {"type": "object"}}}
}

app.openapi_schema = openapi_schema
return app.openapi_schema

app.openapi = custom_openapi

This approach ensures that your custom components are included in the final OpenAPI model before it is serialized and served to documentation UIs.