Skillversitas

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.