Home Python FastAPI Middleware — Logging, CORS and Custom Middleware

FastAPI Middleware — Logging, CORS and Custom Middleware

⚡ Quick Answer
Middleware in FastAPI allows you to intercept every HTTP request and response. Use app.add_middleware() for global configurations like CORS or GZip compression. For custom logic, use the @app.middleware('http') decorator to wrap the call_next function. Key rule: Middleware execution follows an 'Onion' pattern—the last middleware added is the first to receive the request but the last to process the response.

CORS Middleware: Securing Cross-Origin Traffic

CORS is a security feature, not an error. When your frontend (e.g., React on port 3000) tries to talk to your FastAPI backend (port 8000), the browser blocks the request unless the server explicitly permits it. For production, never use ['*']. Always whitelist specific, trusted domains.

io/thecodeforge/middleware/security.py · PYTHON
12345678910111213141516171819202122
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# Configure the 'Onion' layers
# CORSMiddleware should generally be added early in the stack
app.add_middleware(
    CORSMiddleware,
    # List specific trusted origins for production
    allow_origins=[
        'https://api.thecodeforge.io',
        'https://dashboard.thecodeforge.io',
        'http://localhost:3000' 
    ],
    # Required if your frontend sends cookies or Authorization headers
    allow_credentials=True,
    allow_methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    allow_headers=['Authorization', 'X-Forge-Trace-ID', 'Content-Type'],
)

# Note: If allow_credentials is True, allow_origins cannot be ['*']
▶ Output
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://dashboard.thecodeforge.io

Custom Middleware — Performance Logging

Custom middleware uses the call_next pattern. This allows you to run code before the request reaches your route and after the response has been generated. This is the ideal place to calculate 'Time to First Byte' (TTFB) or inject unique request identifiers for log aggregation.

io/thecodeforge/middleware/logging.py · PYTHON
12345678910111213141516171819202122232425262728293031
from fastapi import FastAPI, Request
import time
import uuid
import logging

app = FastAPI()
logger = logging.getLogger("thecodeforge.access")

@app.middleware('http')
async def add_process_time_header(request: Request, call_next):
    # 1. Logic BEFORE the route (Request Phase)
    start_time = time.perf_counter()
    request_id = str(uuid.uuid4())
    
    # Inject trace ID into request state for downstream access
    request.state.trace_id = request_id

    # 2. Hand off to the next middleware or route handler
    response = await call_next(request)

    # 3. Logic AFTER the route (Response Phase)
    process_time = time.perf_counter() - start_time
    
    # Log the performance metric
    logger.info(f"RID: {request_id} | Path: {request.url.path} | Time: {process_time:.4f}s")
    
    # Standardize our response headers
    response.headers['X-Forge-Process-Time'] = str(process_time)
    response.headers['X-Forge-Trace-ID'] = request_id
    
    return response
▶ Output
→ RID: 550e8400... | Path: /api/v1/users | Time: 0.0042s

🎯 Key Takeaways

  • Middleware added last runs first for requests, and last for responses (LIFO order).
  • Global Context: Middleware is protocol-agnostic regarding specific routes; it sees all traffic including 404s and health checks.
  • The 'No Wildcard' Rule: You cannot use allow_origins=['*'] if allow_credentials is set to True due to W3C security specs.
  • State Sharing: Use request.state to pass variables (like user IDs or trace IDs) from middleware into your endpoint logic.
  • Avoid Blocking: Never perform heavy synchronous I/O inside a middleware's async def without utilizing threads, as it will block the entire event loop for all users.

Interview Questions on This Topic

  • QDescribe the execution flow of multiple middlewares in FastAPI. If Middleware A is added before Middleware B, which one sees the Response object first?
  • QWhy is it a security risk to allow all origins ('*') in a production API that uses JWT cookies for authentication?
  • QHow does the ASGI 'scope' differ from the FastAPI 'Request' object, and how would you access it inside a low-level middleware?
  • QExplain the 'Short-Circuiting' behavior: How can a middleware return a response without ever calling the `call_next` function?
  • QScenario: You need to implement IP-based rate limiting. Would you do this in a FastAPI middleware or a Dependency? Justify your choice based on performance and 'Route Matching' logic.

Frequently Asked Questions

What is the difference between middleware and a Depends() dependency?

Execution timing and scope are the key differences. Middleware executes at the 'Gateway' level before FastAPI even figures out which route should handle the request. This makes it perfect for logging and CORS. Dependencies (Depends()) execute after the route is matched but before the business logic runs. Use dependencies for logic that requires access to route parameters or endpoint-specific data.

Why am I getting CORS errors even after adding CORSMiddleware?

This usually happens due to one of three reasons: 1. You have allow_credentials=True but used a wildcard * for origins. 2. Your frontend is sending a custom header (like X-Requested-With) that isn't included in your allow_headers list. 3. Order of operations: If you have another middleware that returns a response (like an Auth check) before the CORSMiddleware is reached, the CORS headers won't be attached to the error response.

Is there a limit to how many middlewares I can add?

While there is no hard limit, each middleware layer adds a small amount of overhead to the request/response cycle. If you have 20+ middlewares, you may see an increase in latency. For complex transformations, consider moving logic into a background task or an external API gateway like Nginx or Kong.

🔥
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousFastAPI File Uploads and Form DataNext →FastAPI Testing with pytest and TestClient
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged