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
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
rust
use axum::{ extract::{Path, State}, response::sse::{Event, KeepAlive, Sse}, };
Streaming import progress with SSE in Axum
server-sent-events
streams
watch-channel
Advanced
7 steps
javascript
import { useState, useEffect, useCallback, useRef } from 'react'; const cache = new Map(); const inflight = new Map();
Building a stale-while-revalidate hook in React
caching
request-deduplication
custom-hooks
Advanced
10 steps
python
import random from typing import Iterator, List
How reservoir sampling picks k items
reservoir-sampling
streaming
randomness
Intermediate
5 steps
python
import secrets from django.contrib.auth import authenticate, login from django.core.cache import cache
Two-factor login with OTP in Django
two-factor-auth
one-time-passwords
caching
Intermediate
9 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.