Skip to main content

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 FastAPI Request object.
  • call_next: A function that receives the request as a parameter and passes it to the corresponding path operation, returning the response.
  • 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

FeatureGlobal DependenciesMiddleware
LevelFastAPI (High-level)ASGI (Low-level)
AccessParams, Security, DB SessionsRequest/Response Headers, Body
Best ForAuth, Validation, PermissionsCORS, GZip, Timing, Logging
ExceptionsHandled by FastAPI Exception HandlersMust be handled within the middleware

Execution Order

  1. Middleware Stack: Middleware functions like a stack. The last middleware added with app.add_middleware() or @app.middleware is the outermost layer. It is the first to receive the request and the last to process the response.
  2. 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 contextvars in a dependency with yield, 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 FileResponse or StreamingResponse) 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.