I built an open-source library: fastapi-middlewares

I kept copying the same middleware setup code across every FastAPI project. Request IDs, timing, security headers, logging—same stuff, different project.
So I built FastAPI-Middlewares to fix this. It sets up production-ready middlewares in two lines of code.
What It Does
FastAPI-Middlewares gives you the core middlewares you need for production. Nothing fancy, just the basics done right.
What's included:
- 🔍 Request ID Tracking — Unique IDs for tracing requests
- ⏱️ Request Timing — Measure how long requests take
- 🔒 Security Headers — OWASP-compliant headers
- 📝 Structured Logging — JSON-formatted logs
- 🚨 Error Handling — Clean error responses
- ⚡ Easy Setup — One function call with smart defaults
Install It
pip install fastapi-middlewaresOr with uv:
uv add fastapi-middlewaresQuick Start
from fastapi import FastAPI
from middlewares import add_essentials
app = FastAPI()
# That's it—one line
add_essentials(app)
@app.get("/")
def root():
return {"message": "Hello World"}Now your app has:
- ✅ Request ID tracking
- ✅ Request timing
- ✅ Security headers
- ✅ CORS support
- ✅ Error handling
- ✅ Logging
- ✅ GZip compression
The Middlewares
1. Request ID Middleware
Adds a unique ID to each request so you can trace it through logs.
from fastapi import FastAPI, Request
from middlewares import RequestIDMiddleware
app = FastAPI()
app.add_middleware(RequestIDMiddleware)
@app.get("/users/{user_id}")
def get_user(user_id: int, request: Request):
request_id = request.scope.get("request_id")
return {"user_id": user_id, "request_id": request_id}Response Headers:
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
Options:
header_name: Custom header name (default: "X-Request-ID")
2. Request Timing Middleware
Shows how long each request took.
from middlewares import RequestTimingMiddleware
app.add_middleware(RequestTimingMiddleware)Response Headers:
X-Process-Time: 0.0245
Options:
header_name: Custom header name (default: "X-Process-Time")
3. Security Headers Middleware
Adds headers to protect against common attacks.
from middlewares import SecurityHeadersMiddleware
app.add_middleware(SecurityHeadersMiddleware)Default Headers:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 0
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: ...
Strict-Transport-Security: ... (HTTPS only)
Custom Headers:
app.add_middleware(
SecurityHeadersMiddleware,
headers={
"X-Frame-Options": "SAMEORIGIN",
"X-Custom-Header": "custom-value"
}
)Options:
headers: Dict of custom headershsts_max_age: HSTS max age in seconds (default: 31536000)
4. Logging Middleware
Logs all requests and responses in a structured format.
from middlewares import LoggingMiddleware
app.add_middleware(
LoggingMiddleware,
logger_name="my_app",
skip_paths=["/health", "/metrics"]
)Log Output:
{
"request_id": "550e8400-...",
"method": "GET",
"path": "/users/123",
"status_code": 200,
"process_time": "0.0245s"
}Options:
logger_name: Logger name (default: "fastapi_middlewares")skip_paths: Paths to skip logging (default: ["/health", "/metrics"])
5. Error Handling Middleware
Catches exceptions and returns clean JSON errors.
from middlewares import ErrorHandlingMiddleware
app.add_middleware(
ErrorHandlingMiddleware,
include_traceback=False # Set True for development
)Error Response:
{
"error": "ValueError",
"message": "Invalid user ID",
"request_id": "550e8400-..."
}Custom Error Handlers:
from starlette.responses import JSONResponse
async def handle_value_error(scope, exc):
return JSONResponse(
status_code=400,
content={"error": "bad_request", "message": str(exc)}
)
app.add_middleware(
ErrorHandlingMiddleware,
custom_handlers={ValueError: handle_value_error}
)Options:
include_traceback: Include full traceback (default: False)custom_handlers: Dict mapping exception types to handler functions
6. CORS Middleware
Wrapper around Starlette's CORSMiddleware.
from middlewares import add_cors
add_cors(
app,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"]
)7. GZip Compression
Wrapper around Starlette's GZipMiddleware.
from middlewares import add_gzip
add_gzip(app, minimum_size=1000)Middleware Order Matters
Middlewares run in reverse order of how you add them. Last added = first to run.
Recommended Order:
from fastapi import FastAPI
from middlewares import (
ErrorHandlingMiddleware,
SecurityHeadersMiddleware,
RequestIDMiddleware,
RequestTimingMiddleware,
LoggingMiddleware,
add_cors,
add_gzip,
)
app = FastAPI()
# Last added = First executed
add_gzip(app) # 7. Compress response
app.add_middleware(LoggingMiddleware) # 6. Log request/response
app.add_middleware(RequestTimingMiddleware) # 5. Time request
app.add_middleware(RequestIDMiddleware) # 4. Add request ID
app.add_middleware(SecurityHeadersMiddleware) # 3. Add security headers
add_cors(app) # 2. Handle CORS
app.add_middleware(ErrorHandlingMiddleware) # 1. Catch errors (outermost)Why This Order?
- Error handling first — Catches all exceptions from other middlewares
- CORS early — Handles preflight requests before processing
- Security headers — Added to all responses
- Request ID — Available for all downstream middlewares and logging
- Timing — Measures full request duration
- Logging — Logs complete request/response cycle
- Compression last — Compresses the final response body
Complete Example
from fastapi import FastAPI, HTTPException
from middlewares import add_essentials
app = FastAPI(title="My API")
# Add all middlewares with custom config
add_essentials(
app,
cors_origins=["http://localhost:3000"],
include_traceback=False, # Set True for development
logger_name="my_api"
)
@app.get("/")
def root():
return {"message": "Hello World"}
@app.get("/users/{user_id}")
def get_user(user_id: int):
if user_id < 1:
raise ValueError("Invalid user ID")
return {"user_id": user_id, "name": "John"}
@app.get("/error")
def error():
raise HTTPException(status_code=404, detail="Not found")Run it:
uvicorn main:app --reloadTest it:
# Check headers
curl -I http://localhost:8000/
# Expected headers:
# X-Request-ID: 550e8400-...
# X-Process-Time: 0.0245
# X-Content-Type-Options: nosniff
# X-Frame-Options: DENYWrap Up
If this saves you time, star the repo on GitHub:
github.com/mahdijafaridev/fastapi-middlewares
Want to improve it? Send a PR or open an issue.
Open source gets better when more people contribute.
Related Articles

Make Your FastAPI Responses Up to 5x Faster with One Simple Change
See how replacing FastAPI’s default JSON serializer can drastically improve your API’s response time and reduce server load

Soft Skills for Software Engineers: Unlocking Success Beyond Code
Why soft skills are more important for software engineers and how to develop them.

10 Lessons I Learned from 6 Years of Being a Software Engineer
Reflecting on the key lessons I've learned over six years in software engineering.