Skip to main content

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:

  • BackgroundTasks is 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 Request and a call_next function.
  • call_next passes 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.

  1. The last middleware added is the first one to receive the request (it wraps all others).
  2. 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.