Skip to main content

Operations and Path Items

FastAPI automatically generates an OpenAPI schema for your application by inspecting your route decorators, function signatures, and return types. This process involves mapping FastAPI's internal routing structures to the OpenAPI specification's models, specifically Path Items and Operations.

Mapping Routes to Path Items

In OpenAPI, a Path Item represents a single relative path (e.g., /items/{item_id}). A single PathItem can contain multiple Operations, such as GET, POST, or DELETE.

When you define a route in FastAPI:

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}

FastAPI's get_openapi_path function (in fastapi/openapi/utils.py) creates a PathItem for the path /items/{item_id}. Internally, the PathItem class (from fastapi.openapi.models) holds these operations in fields named after the HTTP methods:

# Internal representation in fastapi.openapi.models.PathItem
class PathItem(BaseModelWithConfig):
get: Operation | None = None
put: Operation | None = None
post: Operation | None = None
# ... other methods

Operations and Metadata

Each HTTP method on a path is represented by an Operation object. FastAPI populates the Operation metadata using information from your path operation decorator and function.

Summary and Description

FastAPI generates a summary from your function name (converting underscores to spaces and title-casing it) and uses the function's docstring or the description parameter for the description field.

@app.get("/items/", summary="List all items", description="Retrieve a list of items from the database")
def read_items():
return []

Internally, get_openapi_operation_metadata handles this mapping:

# From fastapi/openapi/utils.py
operation["summary"] = generate_operation_summary(route=route, method=method)
if route.description:
operation["description"] = route.description

Operation IDs

The operationId is a unique identifier for the operation. FastAPI generates this automatically using your function name and path, but you can override it. If duplicate IDs are detected, FastAPI issues a warning during schema generation.

@app.get("/items/", operation_id="custom_list_items")
def read_items():
return []

Request Bodies and Media Types

When a route expects a body (e.g., via a Pydantic model or Body() parameter), FastAPI constructs a RequestBody object.

RequestBody and MediaType

The RequestBody contains a content dictionary where keys are media types (like application/json) and values are MediaType objects.

from pydantic import BaseModel
from fastapi import Body

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

@app.post("/items/")
def create_item(item: Item):
return item

FastAPI uses get_openapi_operation_request_body to build this structure. The MediaType object includes the JSON Schema for the body:

# Internal representation in fastapi.openapi.models.MediaType
class MediaType(BaseModelWithConfig):
schema_: Schema | Reference | None = Field(default=None, alias="schema")
example: Any | None = None
examples: dict[str, Example | Reference] | None = None
encoding: dict[str, Encoding] | None = None

Complex Encodings

For multipart/form-data or application/x-www-form-urlencoded requests, FastAPI uses the Encoding class to define how individual fields should be serialized. This is common when uploading files alongside form data.

Responses and Status Codes

FastAPI automatically generates a default Response for your route's success status code (usually 200 or the value in status_code).

Default and Additional Responses

You can define additional responses using the responses parameter. FastAPI merges these into the Operation.responses dictionary.

@app.get("/items/{item_id}", responses={404: {"description": "Item not found"}})
def read_item(item_id: str):
if item_id == "portal-gun":
return {"item_id": item_id}
return JSONResponse(status_code=404, content={"message": "Not found"})

If your route has parameters or a body, FastAPI automatically adds a 422 Unprocessable Entity response to the OpenAPI schema to describe validation errors, unless you provide your own 422 or 4XX response.

Advanced Customization with openapi_extra

If you need to add fields to the OpenAPI schema that FastAPI doesn't support natively (like specification extensions starting with x-), use the openapi_extra parameter in your path decorator.

@app.get("/", openapi_extra={"x-custom-extension": "internal-use-only"})
def root():
return {"message": "Hello World"}

FastAPI uses deep_dict_update to merge your openapi_extra dictionary directly into the generated Operation object:

# From fastapi/openapi/utils.py
if route.openapi_extra:
deep_dict_update(operation, route.openapi_extra)

This allows you to override any part of the generated operation or add custom metadata required by third-party tools.