Skip to main content

Essential middlewares for FastAPI

Project description

FastAPI Middlewares

Essential middlewares for FastAPI applications.

CI codecov PyPI version Python Versions License: MIT

Features

  • 🔍 Request ID Tracking - Unique IDs for request tracing
  • ⏱️ Request Timing - Measure response times
  • 🔒 Security Headers - OWASP-compliant security headers
  • 📝 Structured Logging - JSON-formatted request/response logs
  • 🚨 Error Handling - Graceful error responses with tracebacks
  • Easy Setup - One-line configuration with sensible defaults

Installation

pip install fastapi-middlewares

Or with uv:

uv add fastapi-middlewares

Quick Start

from fastapi import FastAPI
from middlewares import add_essentials

app = FastAPI()

# Add all essential middlewares in one line
add_essentials(app)

@app.get("/")
def root():
    return {"message": "Hello World"}

That's it! Your app now has:

  • ✅ Request ID tracking
  • ✅ Request timing
  • ✅ Security headers
  • ✅ CORS support
  • ✅ Error handling
  • ✅ Logging
  • ✅ GZip compression

Middlewares

1. Request ID Middleware

Adds a unique ID to each request for tracing.

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

Tracks how long each request takes.

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 security 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 headers
  • hsts_max_age: HSTS max age in seconds (default: 31536000)

4. Logging Middleware

Logs all requests and responses with structured output.

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 formatted 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 Ordering

Order matters! Middlewares execute in reverse order of addition.

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?

  1. Error handling first - Catches all exceptions from other middlewares
  2. CORS early - Handles preflight requests before processing
  3. Security headers - Added to all responses
  4. Request ID - Available for all downstream middlewares and logging
  5. Timing - Measures full request duration
  6. Logging - Logs complete request/response cycle
  7. 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 --reload

Test 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: DENY

Development

# Clone the repo
git clone https://github.com/mahdijafaridev/fastapi-middlewares.git
cd fastapi-middlewares

# Install dependencies
uv sync

# Run tests
pytest -v

# Run with coverage
pytest --cov=middlewares --cov-report=html

# Run example app
python examples/example_app.py

Contributing

Contributions are welcome! Please read CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE file for details.

Credits

Built with ❤️ for the FastAPI community.

Changelog

See CHANGELOG.md for release history.

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastapi_middlewares-0.1.0.tar.gz (13.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_middlewares-0.1.0-py3-none-any.whl (7.4 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_middlewares-0.1.0.tar.gz.

File metadata

  • Download URL: fastapi_middlewares-0.1.0.tar.gz
  • Upload date:
  • Size: 13.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for fastapi_middlewares-0.1.0.tar.gz
Algorithm Hash digest
SHA256 6af851dd5daac972d433a42664fd553dc084abe86d247232993a4c3b6e268b8d
MD5 ff6a62cb56aeac04f88d6255f7d93ee3
BLAKE2b-256 78c84b71cbd4377c5803fd1449d308f0f78badc82b300fe975624dffdafc625c

See more details on using hashes here.

File details

Details for the file fastapi_middlewares-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_middlewares-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b0163885901c347fc81face64b94f8548eb8b1188ed3cd5bb304566972375da7
MD5 f7ec2b12379f622033dd146237518dab
BLAKE2b-256 64618127c84c1bbbad4fd2e4eeb30aa8ca75b2a57d1f9bc16dc67c0ca6089789

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page