agentbench / data /tech_docs /fastapi_middleware.md
Nomearod's picture
feat: Day 4 — corpus, ingest script, first 10 golden questions
a152b95
# Middleware in FastAPI
Middleware is a function that processes every request before it reaches a route handler and every response before it is returned to the client. FastAPI supports both ASGI middleware (from Starlette) and its own decorator-based middleware.
## Custom Middleware
Use the `@app.middleware("http")` decorator to create custom middleware:
```python
import time
from fastapi import FastAPI, Request
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.perf_counter()
response = await call_next(request)
process_time = time.perf_counter() - start_time
response.headers["X-Process-Time"] = f"{process_time:.4f}"
return response
```
The middleware receives the incoming `Request` object and a `call_next` function. Calling `await call_next(request)` passes the request to the next middleware or route handler in the chain and returns the `Response`. You can modify both the request (before `call_next`) and the response (after `call_next`).
## CORS Middleware
Cross-Origin Resource Sharing (CORS) is configured using `CORSMiddleware` from Starlette:
```python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://example.com", "https://app.example.com"],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
expose_headers=["X-Custom-Header"],
max_age=600,
)
```
The `CORSMiddleware` parameters:
| Parameter | Default | Description |
|----------------------|---------|----------------------------------------------------|
| `allow_origins` | `[]` | List of allowed origin URLs |
| `allow_origin_regex` | `None` | Regex pattern for matching allowed origins |
| `allow_methods` | `["GET"]` | HTTP methods allowed for cross-origin requests |
| `allow_headers` | `[]` | HTTP headers allowed in cross-origin requests |
| `allow_credentials` | `False` | Whether cookies are permitted in cross-origin requests |
| `expose_headers` | `[]` | Response headers accessible to the browser |
| `max_age` | `600` | Seconds the browser caches preflight results |
To allow all origins, use `allow_origins=["*"]`. However, when `allow_credentials=True`, you cannot use the wildcard `"*"` for `allow_origins` -- you must list specific origins. This is a CORS specification requirement, not a FastAPI limitation.
## Middleware Ordering
Middleware executes in reverse order of how it is added. The last middleware added is the first to process the request (outermost layer):
```python
app = FastAPI()
@app.middleware("http")
async def middleware_one(request: Request, call_next):
print("Middleware 1: before") # Runs second
response = await call_next(request)
print("Middleware 1: after") # Runs third
return response
@app.middleware("http")
async def middleware_two(request: Request, call_next):
print("Middleware 2: before") # Runs first
response = await call_next(request)
print("Middleware 2: after") # Runs fourth
return response
```
The output order for a request is: `Middleware 2: before`, `Middleware 1: before`, (route handler), `Middleware 1: after`, `Middleware 2: after`. This follows the standard "onion" model where each middleware wraps the next layer.
## Trusted Host Middleware
Protect against HTTP Host header attacks:
```python
from fastapi.middleware.trustedhost import TrustedHostMiddleware
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["example.com", "*.example.com"],
)
```
Requests with a `Host` header not matching the allowed hosts receive a 400 Bad Request response.
## GZip Middleware
Compress responses automatically when the client supports it:
```python
from fastapi.middleware.gzip import GZipMiddleware
app.add_middleware(GZipMiddleware, minimum_size=500)
```
The `minimum_size` parameter (default: `500` bytes) sets the minimum response body size before compression is applied. Responses smaller than this threshold are sent uncompressed. GZip compression typically reduces JSON response sizes by 60-80%.
## ASGI Middleware
Since FastAPI is an ASGI application, you can use any ASGI-compatible middleware:
```python
from starlette.middleware.sessions import SessionMiddleware
app.add_middleware(
SessionMiddleware,
secret_key="your-session-secret",
max_age=14 * 24 * 60 * 60, # 14 days in seconds = 1,209,600
)
```
The `add_middleware()` method is the preferred way to add middleware in FastAPI, as it ensures proper integration with the application's middleware stack and exception handling.