If you’re building an API with FastAPI, eventually you’ll need to address aspects like logging, authentication, request throttling, or response manipulation—all of which often span multiple routes. Rather than cluttering your endpoints or dependencies, FastAPI middleware lets you inject logic that acts before and after every request. In this article, I’ll show you how to write custom middleware in FastAPI, and share some practical tips for making your middleware effective and maintainable.
What is Middleware in FastAPI?
Middleware sits between the request and your route handlers. Each middleware is a function or class that receives a request before it reaches your API logic, and can also process the response before it goes back to the client.
This pattern is powerful for cross-cutting concerns, such as:
- Logging requests and responses
- Modifying or adding headers
- Authentication and authorization
- Rate limiting
- Error handling
Writing Your First Custom Middleware
Here’s a minimalist example. Suppose you want to log the method and path for each incoming request.
from fastapi import FastAPI, Request
app = FastAPI()
@app.middleware("http")
async def log_requests(request: Request, call_next):
print(f"{request.method} {request.url.path}")
response = await call_next(request)
return response
The middleware receives every HTTP request, logs some information, then passes control to the next element in the processing chain using call_next
.
Going Further: Middleware as a Class
When you need to keep state or configuration, using a class-based middleware is the way to go. FastAPI/Starlette expects ASGI middleware, which look like this:
from starlette.middleware.base import BaseHTTPMiddleware
class AddCustomHeaderMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
response = await call_next(request)
response.headers["X-Custom-Header"] = "Hello, Middleware!"
return response
app.add_middleware(AddCustomHeaderMiddleware)
This wraps all requests and adds an extra header to each response.
Tips for Effective Middleware
- Order matters. Middleware is executed in the order it’s added. If you have multiple layers, remember this can affect things like error propagation and logging.
- Don’t block the event loop. Middleware should be async and avoid synchronous/blocking calls, as this can degrade overall FastAPI performance.
- Handle exceptions carefully. If you’re doing custom error handling in middleware, be sure to re-raise or catch only what you intend to, to avoid swallowing important exceptions.
When (and When Not) to Use Middleware
Middleware is best for logic that must apply to every request. If something is specific to a route or a subset of routes, prefer dependencies—they’re cleaner for per-route custom logic.
Conclusion
Custom middleware in FastAPI empowers you to centralize logic and keep your routes clean and focused. Whether you’re adding headers, logging, or enforcing policies, middleware is the tool for the job. I encourage you to start simple, and reach for class-based middleware when your needs grow.
Happy coding! — Fast Eddy
Leave a Reply