Concept lesson

FastAPI Custom ASGI Middleware

Request processing chains, correlation IDs, ContextVars, and response header mutation.

lesson
Freshness: current14 min read
Mastery
not started · 0%

Learning outcomes

  • Build custom ASGI HTTP middlewares
  • Track correlation IDs safely with ContextVars

Mental model

FastAPI middleware is an outer ASGI wrapper around your application, executing onion-style before and after every HTTP request.

HTTP Request
Outer ASGI Middleware
Trace Correlation ID ContextVar
FastAPI Router & Route Handler
Response Header Mutation
Conceptual teaching model synthesized from:FastAPI Framework Architecture & Dependency Injection Specification

Theory

Custom middlewares can be implemented via @app.middleware("http") or pure ASGI class wrappers (BaseHTTPMiddleware). Middlewares are ideal for cross-cutting concerns: setting unique X-Request-ID correlation headers, logging request duration, handling global CORS policies, and scoping thread-safe request state using Python contextvars.ContextVar.

import time
import uuid
from contextvars import ContextVar
from fastapi import FastAPI, Request

request_id_var: ContextVar[str] = ContextVar("request_id", default="")

app = FastAPI()

@app.middleware("http")
async def add_correlation_id_and_timing(request: Request, call_next):
    correlation_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
    token = request_id_var.set(correlation_id)

    start_time = time.perf_counter()
    response = await call_next(request)
    duration_ms = (time.perf_counter() - start_time) * 1000

    response.headers["X-Request-ID"] = correlation_id
    response.headers["X-Process-Time-MS"] = f"{duration_ms:.2f}"

    request_id_var.reset(token)
    return response

@app.get("/health")
async def health_check():
    return {"status": "ok", "correlation_id": request_id_var.get()}

Alternatives and trade-offs

  • Middlewares: Run globally on every request (CORS, timing, request ID).
  • FastAPI Dependencies: Run only on targeted routes and support Depends graph composition.

Failure modes and misconceptions

  1. BaseHTTPMiddleware Streaming Issues: BaseHTTPMiddleware buffers responses in memory and can break SSE / WebSocket streaming.
  2. ContextVar Leaks: Always reset ContextVar tokens in finally blocks when reusing threads across requests.
Reflect before revealing the guide

Decision scenario

Use @app.middleware("http") for application-wide request logging and correlation headers. Use route-level Depends for granular authentication and authorization checks.

Learning outcomes

  • Build HTTP middlewares for correlation tracing and latency logging.
  • Store context-local state safely across async tasks using contextvars.ContextVar.
  • Avoid response buffering issues with high-throughput streaming endpoints.

Trade-offs

Global middlewares execute on every incoming HTTP request path, so middleware logic must stay extremely fast and lightweight.

Evidence assessment

Theory and decision mastery

not-started · 0%
theory0%
decision0%
activityNot mapped
projectNot mapped
1. What tool cleanly maintains thread-safe request correlation IDs across async function calls in Python?
2. Why can BaseHTTPMiddleware cause issues with Server-Sent Events (SSE) or WebSockets?
3. When should you choose a FastAPI Depends dependency over a global middleware?

Decision scenario

An API team needs to inject a unique X-Request-ID header into every HTTP response and log request duration.

What is the most idiomatic FastAPI implementation?

Primary sources