Overview
Relevant Files
fastapi/init.pyfastapi/applications.pyfastapi/routing.pyfastapi/dependencies/fastapi/middleware/README.md
FastAPI is a modern, high-performance web framework for building APIs with Python. It combines the speed of Starlette with the data validation power of Pydantic, enabling developers to write production-ready APIs with minimal code while maintaining full type safety.
Core Philosophy
FastAPI is built on three key principles: speed (high performance comparable to Node.js and Go), developer experience (fast to code with 200-300% faster development), and robustness (automatic validation and documentation). The framework leverages Python type hints to eliminate boilerplate and reduce bugs by approximately 40%.
Architecture Overview
Loading diagram...
Key Components
FastAPI Application (fastapi/applications.py): The main entry point that extends Starlette. It manages routing, OpenAPI schema generation, documentation endpoints (/docs, /redoc), and middleware configuration. The app initializes with metadata (title, version, description) that automatically populates API documentation.
APIRouter (fastapi/routing.py): Handles path operations and route registration. It supports decorators like @app.get(), @app.post(), etc., and manages dependencies, response models, and OpenAPI metadata for each endpoint.
Dependency Injection (fastapi/dependencies/): A powerful system for managing request dependencies. The solve_dependencies() function recursively resolves dependencies, supports caching, and handles cleanup via AsyncExitStack. Dependencies can be functions, classes, or context managers.
Middleware Stack (fastapi/middleware/): Built on Starlette's middleware system. FastAPI adds AsyncExitStackMiddleware for resource cleanup and wraps user middleware with exception handling. Middleware processes requests before they reach route handlers.
Exception Handling (fastapi/exception_handlers.py): Automatic handling of HTTPException and validation errors with proper HTTP status codes and JSON responses. Custom exception handlers can be registered per exception type.
Request Processing Flow
- Request enters the ASGI middleware stack
- Exception middleware catches and formats errors
- Router matches the request to a path operation
- Dependencies are resolved and injected
- Path operation function executes
- Response is serialized and returned
OpenAPI Integration
FastAPI automatically generates OpenAPI 3.1.0 schemas from your code. The openapi() method introspects routes and generates comprehensive API documentation. This powers interactive UIs like Swagger UI (/docs) and ReDoc (/redoc), enabling automatic client generation and API exploration.
Type Safety & Validation
Pydantic models validate request bodies, query parameters, headers, and path parameters. Type hints enable IDE autocompletion and catch errors before runtime. Response models serialize output with automatic type conversion and validation.
Architecture & Core Components
Relevant Files
fastapi/applications.pyfastapi/routing.pyfastapi/dependencies/models.pyfastapi/dependencies/utils.py
FastAPI is built on top of Starlette and Pydantic, combining them into a cohesive framework for building high-performance APIs. The architecture revolves around three core concepts: the application layer, routing system, and dependency injection.
Application Layer
The FastAPI class extends Starlette and serves as the main entry point. It manages the internal APIRouter which handles all route registration and path operations. The application initializes with metadata (title, version, description), OpenAPI configuration, middleware setup, and lifespan event handlers. FastAPI delegates most routing logic to its internal router while providing convenient decorator methods like @app.get(), @app.post(), etc.
Routing System
FastAPI uses two main route classes: APIRoute for HTTP endpoints and APIWebSocketRoute for WebSocket connections. Each route is defined by a path, HTTP methods, and an endpoint function. Routes are compiled into regex patterns for efficient matching using Starlette's compile_path() function.
When a request arrives, the router matches it against registered routes and extracts path parameters. The APIRoute class stores metadata like response models, status codes, tags, and OpenAPI documentation. It also creates a Dependant object that represents the endpoint's dependency tree.
Dependency Injection System
The dependency injection system is the heart of FastAPI's request handling. The Dependant dataclass represents a callable with its parameters categorized as:
- Path parameters - extracted from the URL path
- Query parameters - from query strings
- Header parameters - from HTTP headers
- Cookie parameters - from cookies
- Body parameters - from request body
- Dependencies - sub-dependants resolved recursively
The get_dependant() function analyzes a callable's signature and builds a dependency tree. It recursively processes Depends() annotations to create nested dependencies. The get_flat_dependant() function flattens this tree for efficient processing.
Request Handling Pipeline
When a request is processed:
- Dependency Resolution -
solve_dependencies()recursively resolves all dependencies, validating parameters against their type annotations using Pydantic - Caching - Dependencies are cached per request using a
cache_keybased on the callable and OAuth scopes - Execution - The endpoint function is called with resolved values
- Response Serialization - The response is validated against the
response_modeland serialized to JSON
Loading diagram...
Key Design Patterns
Async-First: All request handlers are async-compatible. Sync functions are automatically wrapped with run_in_threadpool() to avoid blocking.
Type-Driven: Pydantic models and type annotations drive validation, serialization, and OpenAPI schema generation automatically.
Composable: Dependencies can depend on other dependencies, enabling middleware-like patterns without explicit middleware registration.
Routing & Path Operations
Relevant Files
fastapi/routing.pyfastapi/param_functions.pyfastapi/utils.py
FastAPI's routing system maps HTTP requests to endpoint functions using path patterns and HTTP methods. The core components are APIRoute (individual routes), APIRouter (route collections), and parameter functions that extract and validate data from requests.
Route Definition & Matching
Routes are defined using decorators on APIRouter or FastAPI instances:
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
When a request arrives, FastAPI uses Starlette's compile_path() to convert path patterns into regex patterns. Path parameters like {item_id} are extracted using regex matching. The get_path_param_names() utility in utils.py identifies all parameters in a path by finding text within curly braces.
APIRoute Class
APIRoute extends Starlette's Route class and adds FastAPI-specific features:
- Path compilation: Converts
{param}syntax to regex patterns and parameter converters - Dependency resolution: Builds a
Dependanttree representing all endpoint dependencies - Response validation: Validates responses against the declared
response_model - OpenAPI generation: Stores metadata (tags, description, status codes) for documentation
The route handler is created by get_request_handler(), which orchestrates request parsing, dependency injection, endpoint execution, and response serialization.
APIRouter & Route Organization
APIRouter groups related routes and can be included in other routers or the main app:
router = APIRouter(prefix="/items", tags=["items"])
@router.get("/")
async def list_items():
return []
app.include_router(router)
When including a router, FastAPI merges configurations: prefixes are concatenated, tags and dependencies are combined, and response definitions are inherited. This enables modular application structure.
Parameter Functions
FastAPI provides parameter functions to declare where data comes from:
Path(): Extracts from URL path (always required)Query(): Extracts from query string (optional by default)Header(): Extracts from HTTP headersCookie(): Extracts from cookiesBody(): Extracts from request body
Each function accepts validation constraints (gt, le, min_length, pattern, etc.) and OpenAPI metadata:
from typing import Annotated
from fastapi import Path, Query
@app.get("/items/{item_id}")
async def read_item(
item_id: Annotated[int, Path(gt=0)],
q: Annotated[str | None, Query(max_length=50)] = None
):
return {"item_id": item_id, "q": q}
Request Processing Pipeline
For each request, FastAPI:
- Matches the path against registered routes using regex patterns
- Extracts path parameters and converts them to declared types
- Resolves dependencies (including parameter extraction)
- Calls the endpoint function with resolved values
- Validates and serializes the response
- Returns the HTTP response
The solve_dependencies() function handles the complex dependency graph, supporting nested dependencies, caching, and cleanup via context managers.
Loading diagram...
Dependency Injection System
Relevant Files
fastapi/dependencies/utils.pyfastapi/dependencies/models.pyfastapi/params.py
FastAPI's dependency injection system is a powerful mechanism for managing request dependencies, enabling code reuse, security enforcement, and resource management. It automatically resolves and injects dependencies into path operation functions.
Core Components
Dependant Model (fastapi/dependencies/models.py): The Dependant dataclass represents a callable with its parameters categorized into:
- Path parameters - extracted from URL paths
- Query parameters - from query strings
- Header parameters - from HTTP headers
- Cookie parameters - from cookies
- Body parameters - from request body
- Dependencies - sub-dependants resolved recursively
The Dependant class also tracks special parameters like Request, Response, BackgroundTasks, and SecurityScopes.
Depends Declaration (fastapi/params.py): The Depends class is a frozen dataclass that wraps a callable dependency:
@dataclass(frozen=True)
class Depends:
dependency: Optional[Callable[..., Any]] = None
use_cache: bool = True
scope: Union[Literal["function", "request"], None] = None
The scope parameter controls when dependencies execute: "function" (default) runs before the path operation, while "request" runs around the entire request-response cycle.
Dependency Resolution
get_dependant() analyzes a callable's signature and builds a dependency tree. It:
- Inspects function parameters using
get_typed_signature() - Identifies
Depends()annotations to create nested dependencies - Categorizes parameters (path, query, header, cookie, body)
- Recursively processes sub-dependencies
solve_dependencies() executes the dependency tree at runtime:
async def solve_dependencies(
*,
request: Union[Request, WebSocket],
dependant: Dependant,
body: Optional[Union[dict[str, Any], FormData]] = None,
background_tasks: Optional[StarletteBackgroundTasks] = None,
response: Optional[Response] = None,
dependency_overrides_provider: Optional[Any] = None,
dependency_cache: Optional[dict[DependencyCacheKey, Any]] = None,
async_exit_stack: AsyncExitStack,
embed_body_fields: bool,
) -> SolvedDependency:
This function recursively resolves all dependencies, supports caching via dependency_cache, and handles cleanup through AsyncExitStack.
Key Features
Caching: Dependencies with use_cache=True are cached per request using a DependencyCacheKey tuple containing the callable, OAuth scopes, and computed scope. This prevents redundant execution.
Dependency Overrides: The dependency_overrides_provider allows replacing dependencies at runtime, useful for testing. When an override exists, get_dependant() is re-invoked with the override callable.
Generator Support: Dependencies can be generator functions (sync or async) that yield a value and perform cleanup. The system wraps them with context managers and manages their lifecycle via AsyncExitStack.
Scope Management: Dependencies respect scope boundaries—a "request"-scoped dependency cannot depend on "function"-scoped dependencies, enforced via DependencyScopeError.
Execution Flow
Loading diagram...
Common Patterns
Dependencies enable shared logic across endpoints:
async def get_current_user(token: str = Depends(oauth2_scheme)):
return await validate_token(token)
@app.get("/items/")
async def read_items(user: User = Depends(get_current_user)):
return user.items
Dependencies can be nested arbitrarily deep, and FastAPI automatically flattens the tree for efficient processing using get_flat_dependant().
Request & Response Handling
Relevant Files
fastapi/requests.pyfastapi/responses.pyfastapi/datastructures.pyfastapi/encoders.pyfastapi/routing.py
FastAPI handles incoming HTTP requests and outgoing responses through a well-structured pipeline that integrates request parsing, validation, serialization, and response formatting. This section covers the core mechanisms that power this flow.
Request Handling
FastAPI builds on Starlette's request infrastructure. The Request class (re-exported from Starlette) provides access to HTTP details like headers, query parameters, path parameters, and the request body. When a request arrives, FastAPI's routing layer extracts the body and parses it based on the endpoint's type annotations.
Body Parsing:
- JSON bodies are automatically parsed when the
Content-Typeheader indicates JSON - Form data is handled via
request.form()for multipart/form-data submissions - Raw bytes are available via
request.body()for custom processing
File Uploads:
The UploadFile class (in fastapi/datastructures.py) wraps Starlette's upload handling with async-friendly methods. It provides:
filename: The original uploaded filenamecontent_type: The MIME type from headersfile: A standard Python file object for sync coderead()andwrite()methods for async operations
Response Serialization
FastAPI's response pipeline validates and serializes endpoint return values through the serialize_response() function in routing.py. This function:
- Validates the response against the declared response model
- Serializes Pydantic models using their
serialize()method (Pydantic v2) orjsonable_encoder()(fallback) - Applies filters like
exclude_none,exclude_defaults, andexclude_unsetto control output
JSON Encoding
The jsonable_encoder() function in fastapi/encoders.py converts Python objects to JSON-compatible types. It handles:
- Pydantic models: Converted via
model_dump(mode="json") - Standard types: Dates, UUIDs, IP addresses, Decimals, Enums
- Collections: Sets, deques, and generators converted to lists
- Custom encoders: User-defined encoding functions for specific types
ENCODERS_BY_TYPE = {
bytes: lambda o: o.decode(),
datetime.date: isoformat,
UUID: str,
Enum: lambda o: o.value,
# ... more type mappings
}
Response Classes
FastAPI provides several response classes (from fastapi/responses.py):
- JSONResponse: Standard JSON serialization (from Starlette)
- UJSONResponse: High-performance JSON using the
ujsonlibrary - ORJSONResponse: Ultra-fast JSON using
orjsonwith NumPy support - HTMLResponse, FileResponse, StreamingResponse: For non-JSON content
Custom response classes can override the render() method to control serialization.
Data Structures
FastAPI re-exports Starlette's data structures for common request/response patterns:
- Headers: Case-insensitive header access
- QueryParams: Immutable query string parameters
- FormData: Parsed form fields
- URL: Parsed request URL
- State: Request-scoped storage for middleware and dependencies
Loading diagram...
Error Handling
Request and response validation errors are caught and converted to HTTP exceptions:
- RequestValidationError (422): Invalid request body or parameters
- ResponseValidationError: Endpoint returned data that doesn't match the response model
Both include detailed error information for debugging.
Security & Authentication
Relevant Files
fastapi/security/base.pyfastapi/security/oauth2.pyfastapi/security/http.pyfastapi/security/api_key.pyfastapi/security/utils.py
FastAPI provides a comprehensive security module that integrates authentication and authorization into your API through the dependency injection system. All security schemes are implemented as callable dependencies that can be used with Depends().
Core Architecture
The security system is built on a base class hierarchy. SecurityBase defines the minimal interface with a model (OpenAPI schema) and scheme_name. All authentication schemes inherit from this and implement the __call__ method to extract credentials from requests.
Security schemes are designed to work with FastAPI's dependency system, allowing you to compose multiple authentication methods and apply them selectively to different endpoints.
HTTP Authentication
FastAPI supports three HTTP authentication schemes:
HTTP Basic (HTTPBasic) extracts username and password from the Authorization header using Base64 encoding. It returns HTTPBasicCredentials containing the decoded username and password.
HTTP Bearer (HTTPBearer) extracts bearer tokens from the Authorization header. It returns HTTPAuthorizationCredentials with the scheme and credentials separated.
HTTP Digest (HTTPDigest) is a stub implementation that integrates with OpenAPI but requires custom implementation of the digest algorithm in your code.
All HTTP schemes support auto_error=False for optional authentication, returning None when credentials are missing instead of raising a 401 error.
API Key Authentication
API keys can be provided in three locations:
- Query Parameter (
APIKeyQuery): Key passed as a query parameter - Header (
APIKeyHeader): Key passed in a custom header - Cookie (
APIKeyCookie): Key passed in a cookie
Each returns the key value as a string. The auto_error parameter controls whether missing keys trigger a 401 error or return None.
OAuth2 Flows
OAuth2PasswordBearer implements the password flow, extracting bearer tokens from the Authorization header. It requires a tokenUrl pointing to your token endpoint and optionally accepts scopes and refreshUrl.
OAuth2AuthorizationCodeBearer implements the authorization code flow with authorizationUrl, tokenUrl, and optional refreshUrl and scopes.
OAuth2PasswordRequestForm is a dependency class that collects username, password, and optional scopes from form data, following the OAuth2 specification. Use OAuth2PasswordRequestFormStrict to enforce the grant_type=password requirement.
SecurityScopes is a special dependency that collects all OAuth2 scopes required by dependencies in the same chain, accessible via scopes (list) and scope_str (space-separated string).
Error Handling
All schemes implement make_not_authenticated_error() returning a 401 HTTPException with appropriate WWW-Authenticate headers. OAuth2 uses Bearer by default; HTTP schemes use their respective scheme names; API Key uses a custom APIKey challenge.
The get_authorization_scheme_param() utility parses the Authorization header, splitting it into scheme and credentials at the first space.
Integration Pattern
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
app = FastAPI()
security = HTTPBearer()
@app.get("/protected")
async def protected_route(
credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
):
return {"token": credentials.credentials}
Combine multiple schemes with auto_error=False to support fallback authentication methods. The dependency system ensures credentials are validated before your endpoint executes.
OpenAPI & Auto-Generated Documentation
Relevant Files
fastapi/openapi/utils.pyfastapi/openapi/models.pyfastapi/openapi/docs.pyfastapi/applications.py
FastAPI automatically generates OpenAPI (formerly Swagger) schemas from your route definitions and type annotations. This enables interactive API documentation and client code generation without manual specification.
Schema Generation Pipeline
The get_openapi() function in fastapi/openapi/utils.py orchestrates the entire schema generation process:
- Route Analysis: Extracts all routes and their metadata (tags, summaries, descriptions, deprecated status)
- Parameter Extraction: Processes path, query, header, and cookie parameters from route dependencies
- Request/Response Modeling: Generates JSON schemas for request bodies and response models
- Security Definitions: Collects OAuth2, API key, and other security schemes
- Schema Assembly: Combines all components into a complete OpenAPI 3.1.0 document
openapi_schema = app.openapi()
# Returns a dict with structure:
# {
# "openapi": "3.1.0",
# "info": {...},
# "paths": {...},
# "components": {"schemas": {...}, "securitySchemes": {...}},
# "webhooks": {...}
# }
Interactive Documentation UIs
FastAPI provides two built-in documentation interfaces via fastapi/openapi/docs.py:
Swagger UI (default at /docs): Interactive API explorer with try-it-out functionality. Configured via swagger_ui_parameters and swagger_ui_init_oauth for OAuth2 flows.
ReDoc (at /redoc): Alternative read-only documentation with better organization for large APIs.
Both are generated as HTML responses that load the OpenAPI schema from the /openapi.json endpoint.
Customization Points
app = FastAPI(
title="My API",
version="1.0.0",
openapi_url="/api/openapi.json", # Custom schema endpoint
docs_url="/api/docs", # Custom Swagger UI location
redoc_url="/api/redoc", # Custom ReDoc location
openapi_tags=[ # Tag metadata
{"name": "items", "description": "Item operations"}
],
servers=[{"url": "https://api.example.com"}],
separate_input_output_schemas=True # Separate schemas for request/response
)
# Override the entire schema
def custom_openapi():
if not app.openapi_schema:
app.openapi_schema = get_openapi(...)
app.openapi_schema["info"]["x-custom"] = "value"
return app.openapi_schema
app.openapi = custom_openapi
Schema Caching
The generated schema is cached in app.openapi_schema after first generation. This avoids expensive re-computation on every request. Disable documentation endpoints by setting openapi_url=None to skip schema generation entirely.
Model Integration
Pydantic models automatically generate JSON schemas. FastAPI uses these to populate the components/schemas section. The model_name_map ensures consistent naming across the schema, and field_mapping handles separate input/output schemas for request and response validation.
Middleware & Exception Handling
Relevant Files
fastapi/exception_handlers.pyfastapi/exceptions.pyfastapi/middleware/asyncexitstack.pyfastapi/applications.py
FastAPI provides a robust system for handling errors and processing requests through middleware. The framework combines Starlette's middleware infrastructure with FastAPI-specific exception handlers and resource cleanup mechanisms.
Exception Handling Architecture
FastAPI registers three default exception handlers that are automatically applied:
- HTTPException Handler - Converts
HTTPExceptionto JSON responses with proper status codes and headers - RequestValidationError Handler - Returns 422 status with detailed validation error information
- WebSocketRequestValidationError Handler - Closes WebSocket connections with policy violation code
Custom exception handlers can be registered using the @app.exception_handler() decorator or passed during app initialization:
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(HTTPException)
async def custom_http_exception_handler(request, exc):
return JSONResponse(
status_code=exc.status_code,
content={"detail": exc.detail}
)
Exception handlers receive the request and exception instance, and must return a Response. They can be registered for specific exception types or HTTP status codes.
Middleware Stack Organization
FastAPI builds a layered middleware stack with a specific order to ensure proper exception handling and resource cleanup:
Loading diagram...
The ServerErrorMiddleware catches unhandled exceptions (500 errors), user middleware runs next, then ExceptionMiddleware handles registered exceptions, and finally AsyncExitStackMiddleware manages resource cleanup.
AsyncExitStackMiddleware
FastAPI includes a custom AsyncExitStackMiddleware that manages resource cleanup after request processing. This middleware:
- Stores an
AsyncExitStackin the request scope underfastapi_middleware_astack - Ensures files and other resources are properly closed after the response is sent
- Preserves context variables across middleware boundaries
- Operates inside user middleware to maintain proper context isolation
async with AsyncExitStack() as stack:
scope["fastapi_middleware_astack"] = stack
await self.app(scope, receive, send)
Custom Exception Handlers
You can override default handlers or add handlers for custom exceptions:
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
@app.exception_handler(RequestValidationError)
async def validation_handler(request, exc):
errors = exc.errors()
message = "\\n".join(
f"Field: {e['loc']}, Error: {e['msg']}"
for e in errors
)
return PlainTextResponse(message, status_code=400)
Handlers can call the default handlers to wrap their behavior, allowing you to add logging or custom processing while preserving standard response formatting.
Exception Flow
When an exception occurs during request processing:
- The exception propagates up through the middleware stack
ExceptionMiddlewarechecks if a handler is registered for that exception type- If found, the handler is called with the request and exception
- The handler returns a Response that is sent to the client
- If no handler exists, the exception propagates to
ServerErrorMiddleware - Resources are cleaned up via
AsyncExitStackMiddlewareregardless of outcome