Middleware
Middleware is code that runs on every request before and after your endpoint: timing, logging, headers, error wrapping, or custom protocol behavior. FastAPI offers three mechanisms: the @app.middleware("http") decorator, pure ASGI middleware, and BaseHTTPMiddleware.
The http Middleware Decorator
import time
from fastapi import FastAPI, Request
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start = time.perf_counter()
response = await call_next(request)
elapsed = time.perf_counter() - start
response.headers["X-Process-Time"] = f"{elapsed:.4f}s"
return response
@app.get("/")
def root():
return {"ok": True}
call_next(request) forwards the request down the stack; you can modify the response (headers, status) before returning it.
BaseHTTPMiddleware
from starlette.middleware.base import BaseHTTPMiddleware
class RequestContext(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
request.state.start = time.perf_counter()
response = await call_next(request)
print(request.method, request.url.path, f"{(time.perf_counter()-request.state.start)*1000:.1f}ms")
return response
app.add_middleware(RequestContext)
request.state is a per-request scratchpad you can read in endpoints and dependencies.
Pure ASGI Middleware
For full control (streaming bodies, early rejects) implement the ASGI interface directly:
class TraceMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return
await self.app(scope, receive, send)
Built-in Middlewares
- CORSMiddleware - covered in the CORS chapter.
- GZipMiddleware - compresses responses for clients that accept gzip.
- TrustedHostMiddleware - validates the Host header against an allowlist.
Ordering
Middleware added later runs first (it wraps the outermost layer). Keep auth and request-context middleware early, and logging near the edge so it sees the final status.
Summary
Use @app.middleware("http") for simple before/after logic like timing and headers, BaseHTTPMiddleware when you need request.state or a class, and pure ASGI for streaming or low-level control. Stack built-ins (CORS, GZip, TrustedHost) as needed, and remember that later-added middleware wraps earlier ones.