Background Tasks and Middleware
To execute logic after a response has been sent or to intercept requests and responses globally, use background tasks and the middleware stack.
Executing Background Tasks
To run a function after returning a response, inject BackgroundTasks into your path operation and use the add_task method.
from fastapi import BackgroundTasks, FastAPI
app = FastAPI()
def write_notification(email: str, message=""):
with open("log.txt", mode="w") as email_file:
content = f"notification for {email}: {message}"
email_file.write(content)
@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(write_notification, email, message="some notification")
return {"message": "Notification sent in the background"}
In this example from docs_src/background_tasks/tutorial001_py310.py:
BackgroundTasksis a class (inherited from Starlette) that FastAPI provides via dependency injection.background_tasks.add_task()accepts the function to run and any arguments it requires.- The response is sent to the client immediately, and the task runs afterward.
Using Background Tasks in Dependencies
You can also inject BackgroundTasks into dependency functions. This allows you to register tasks before the path operation logic even begins.
from fastapi import BackgroundTasks, Depends, FastAPI
app = FastAPI()
def write_log(message: str):
with open("log.txt", mode="a") as log:
log.write(message)
def get_query(background_tasks: BackgroundTasks, q: str | None = None):
if q:
message = f"found query: {q}\n"
background_tasks.add_task(write_log, message)
return q
@app.post("/send-notification/{email}")
async def send_notification(
email: str, background_tasks: BackgroundTasks, q: str = Depends(get_query)
):
message = f"message to {email}\n"
background_tasks.add_task(write_log, message)
return {"message": "Message sent"}
As shown in docs_src/background_tasks/tutorial002_py310.py, both the dependency get_query and the path operation send_notification share the same BackgroundTasks object and can both add tasks to it.
Creating Custom Middleware
To intercept every request before it reaches a path operation and every response before it leaves the server, use the @app.middleware("http") decorator.
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"] = str(process_time)
return response
In docs_src/middleware/tutorial001_py310.py:
- The middleware function receives the
Requestand acall_nextfunction. call_nextpasses the request to the corresponding path operation (or the next middleware).- You can modify the
response(like adding headers) before returning it.
Using Built-in Middleware
FastAPI provides several built-in middlewares for common tasks like compression and security. These are added using app.add_middleware().
GZip Compression
Use GZipMiddleware to compress responses for clients that support it.
from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddleware
app = FastAPI()
app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=5)
@app.get("/")
async def main():
return "somebigcontent"
Trusted Hosts
Use TrustedHostMiddleware to prevent HTTP Host Header attacks by restricting allowed hosts.
from fastapi import FastAPI
from fastapi.middleware.trustedhost import TrustedHostMiddleware
app = FastAPI()
app.add_middleware(
TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]
)
HTTPS Redirect
To enforce HTTPS by redirecting all incoming HTTP requests, use HTTPSRedirectMiddleware.
from fastapi import FastAPI
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
app = FastAPI()
app.add_middleware(HTTPSRedirectMiddleware)
Middleware Execution Order
When adding multiple middlewares using app.add_middleware(), they are executed in a Last-In, First-Out (LIFO) order for the request phase.
- The last middleware added is the first one to receive the request (it wraps all others).
- The first middleware added is the last one to receive the request before it reaches the path operation.
For the response phase, the order is reversed: the first middleware added is the first to process the response from the path operation, and the last middleware added is the final one to process the response before it is sent to the client.
Troubleshooting: Middleware Performance
Custom middleware defined with @app.middleware("http") uses Starlette's BaseHTTPMiddleware. While convenient, it can introduce overhead in high-concurrency scenarios or cause issues with context propagation (like contextvars) if not handled carefully. For performance-critical logic, consider implementing a pure ASGI middleware class.