python
36 lines · 7 steps
Request ID tracing in FastAPI middleware
Assign every request a unique ID, stash it in a ContextVar, and thread it through logs and responses.
Explained by
highlit
1import logging
2import uuid
3from contextvars import ContextVar
4
5from starlette.middleware.base import BaseHTTPMiddleware
6from starlette.requests import Request
7from starlette.types import ASGIApp
8
9request_id_ctx: ContextVar[str] = ContextVar("request_id", default="-")
10
11
12class RequestIDMiddleware(BaseHTTPMiddleware):
13 def __init__(self, app: ASGIApp, header_name: str = "X-Request-ID") -> None:
14 super().__init__(app)
15 self.header_name = header_name
16
17 async def dispatch(self, request: Request, call_next):
18 request_id = request.headers.get(self.header_name) or uuid.uuid4().hex
19 token = request_id_ctx.set(request_id)
20 request.state.request_id = request_id
21 try:
22 response = await call_next(request)
23 finally:
24 request_id_ctx.reset(token)
25 response.headers[self.header_name] = request_id
26 return response
27
28
29class RequestIDLogFilter(logging.Filter):
30 def filter(self, record: logging.LogRecord) -> bool:
31 record.request_id = request_id_ctx.get()
32 return True
33
34
35def get_request_id() -> str:
36 return request_id_ctx.get()
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A ContextVar carries per-request state safely across async boundaries without passing it through every function.
- 2Always reset a ContextVar with its token in a finally block so leaked values never contaminate later requests.
- 3A logging filter can enrich every record with contextual data pulled from the current execution context.
Related explainers
go
package middleware import ( "errors"
Capping request body size in Gin
middleware
request limits
error handling
Intermediate
5 steps
python
import json import time import queue
Server-Sent Events streaming in Flask
server-sent-events
streaming
pub-sub
Advanced
9 steps
php
<?php namespace App\Http\Middleware;
Idempotency keys in Laravel middleware
idempotency
middleware
caching
Advanced
8 steps
python
from flask import Blueprint, request, jsonify from marshmallow import Schema, fields, validate, ValidationError, EXCLUDE from .models import db, User
How a Flask blueprint validates and creates users
validation
rest-api
schema
Intermediate
8 steps
python
import random import click from faker import Faker
Building a Flask seed command with Click
cli
database seeding
orm
Intermediate
7 steps
rust
use axum::{ extract::{Request, State}, http::{HeaderValue, StatusCode}, middleware::Next,
API version headers with Axum middleware
middleware
http-headers
versioning
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/request-id-tracing-in-fastapi-middleware-explained-python-b331/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.