From Passive FastAPI Developer to Real FastAPI Engineer – Part 3: Middleware and Background Tasks
Exploring ASGI middleware, background tasks, and advanced request/response patterns in FastAPI.
ASGI Middleware in FastAPI
Middleware is a layer that sits between the server and your application. In ASGI, middleware is an async callable that can intercept requests and responses:
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi import FastAPI, Request
app = FastAPI()
class SimpleMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
print(f"Request URL: {request.url}")
response = await call_next(request)
response.headers['X-Custom-Header'] = 'Hello from Middleware'
return response
app.add_middleware(SimpleMiddleware)
Middleware can be used for:
- Logging requests and responses
- Adding custom headers
- Handling authentication and authorization
- Measuring performance or request duration
Background Tasks
FastAPI supports running tasks after sending the response, which is essential for operations that don’t need to block the request:
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def write_log(message: str):
with open("log.txt", "a") as f:
f.write(f"{message}\n")
@app.post("/send-data/")
async def send_data(data: dict, background_tasks: BackgroundTasks):
background_tasks.add_task(write_log, f"Received: {data}")
return {"message": "Data received. Logging in background."}
Benefits:
- Improves response time for users
- Allows long-running tasks to run asynchronously
- Integrates cleanly with async applications
Request and Response Lifecycle
Understanding the request/response lifecycle is key for advanced FastAPI applications:
Client → ASGI Server → Middleware → Path Operation → Response → Middleware → ASGI Server → Client
- Request passes through middleware first
- Path operation executes next
- Response goes back through middleware before returning to client
Middleware can mutate request or response, while background tasks run after the response is sent.
Combining Middleware and Background Tasks
You can combine these features for advanced use cases:
- Logging requests and saving metrics in background
- Authenticating users and sending notifications asynchronously
- Transforming requests and caching responses
@app.post("/upload/")
async def upload_file(file: bytes, background_tasks: BackgroundTasks):
background_tasks.add_task(process_file, file)
return {"status": "File received, processing in background"}
This pattern is essential for high-performance, real-world FastAPI apps.
Best Practices
- Keep middleware lightweight – avoid blocking operations.
- Use background tasks for I/O-heavy operations.
- Leverage ASGI features for streaming and WebSocket scenarios.
- Monitor performance – middleware can introduce latency if overused.