Global Middleware and Dependencies
To apply logic across every request in a FastAPI application, you can use global dependencies or middleware. Global dependencies are ideal for high-level logic like authentication and validation, while middleware provides low-level access to the request and response objects.
Global Dependencies
You can apply dependencies to every path operation in your application by passing a list of Depends objects to the dependencies parameter of the FastAPI class constructor.
from typing import Annotated
from fastapi import Depends, FastAPI, Header, HTTPException
async def verify_token(x_token: Annotated[str, Header()]):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
async def verify_key(x_key: Annotated[str, Header()]):
if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key
# Apply dependencies globally to the entire app
app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])
@app.get("/items/")
async def read_items():
return [{"item": "Portal Gun"}, {"item": "Plumbus"}]
Key Characteristics
- Execution: These run before the path operation logic for every request.
- Return Values: Unlike dependencies defined in a path operation signature, the return values of global dependencies are not injected into your route functions. They are primarily used for side effects (like validation or logging) or to raise exceptions.
- Scope: They apply to all routes, including those in sub-routers included via
app.include_router().
Custom Functional Middleware
For custom logic that needs to modify the request before it reaches a route or modify the response before it is sent, use the @app.middleware("http") decorator.
import time
from typing import Awaitable, Callable
from fastapi import FastAPI, Request, Response
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(
request: Request, call_next: Callable[[Request], Awaitable[Response]]
) -> Response:
start_time = time.time()
# Process the request and get the response from the path operation
response = await call_next(request)
# Modify the response
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
How it Works
request: The standard FastAPIRequestobject.call_next: A function that receives therequestas a parameter and passes it to the corresponding path operation, returning theresponse.- Logic Placement: Code before
await call_next(request)runs before the route; code after it runs after the route has finished.
Class-based Middleware
For complex logic or when using built-in utilities like CORSMiddleware, use the app.add_middleware() method. This is the standard way to integrate standard Starlette or FastAPI middleware classes.
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"http://localhost.tiangolo.com",
"https://localhost.tiangolo.com",
"http://localhost",
"http://localhost:8080",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Comparison and Execution Order
| Feature | Global Dependencies | Middleware |
|---|---|---|
| Level | FastAPI (High-level) | ASGI (Low-level) |
| Access | Params, Security, DB Sessions | Request/Response Headers, Body |
| Best For | Auth, Validation, Permissions | CORS, GZip, Timing, Logging |
| Exceptions | Handled by FastAPI Exception Handlers | Must be handled within the middleware |
Execution Order
- Middleware Stack: Middleware functions like a stack. The last middleware added with
app.add_middleware()or@app.middlewareis the outermost layer. It is the first to receive the request and the last to process the response. - Dependencies: Global dependencies run after the middleware stack has processed the request but before the path operation function is executed.
Troubleshooting Gotchas
- Middleware and ContextVars: Custom middlewares create a new context copy. If you set
contextvarsin a dependency withyield, those values may not be available in the outer layers of the middleware stack. - Streaming Responses: If your middleware modifies the response body, it may break streaming responses (like
FileResponseorStreamingResponse) because the body is consumed to be modified. - Dependency Overrides: Global dependencies can be overridden during testing using
app.dependency_overrides, which is useful for bypassing authentication in test suites.