Custom Middlewares in FastAPI

Last Updated : 28 Mar, 2026

Custom middleware in FastAPI allows injecting logic before and after every request, helping handle cross-cutting concerns like logging, authentication, and response modification without repeating code in each route.

Why use custom middleware?

  • Avoid repetition by centralizing shared logic
  • Ensure consistent behavior across all routes
  • Improve observability with logging and metrics
  • Enforce policies like validation or request blocking

Middleware Execution Flow

To understand custom middleware better, let’s look at the order in which it runs during a request and response cycle.

  • Request path: middlewares are executed in the same order they’re defined (from top to bottom) before request reaches the route handler.
  • Response path: once the route sends back a response, middlewares are triggered again, but this time in reverse order (from bottom to top).
  • Exceptions: if something goes wrong inside a route, the error flows back through middlewares, giving them a chance to log it, handle it or even transform the response.

Adding Custom Middleware

In FastAPI, custom middleware can be added using the @app.middleware("http") decorator. It allows executing logic before and after each request without repeating code in every route.

Basic Syntax:

@app.middleware("http")
async def custom_middleware(request: Request, call_next):
# Code before request reaches the route
response = await call_next(request)
# Code after response is generated
return response

  • request: incoming HTTP request (method, path, headers, body, etc.).
  • call_next(request): forwards request to the next step (another middleware or the route) and returns a response.
  • response: object that you can inspect or modify before sending it back to the client.

Creating Custom Middleware

Example 1: This code defines a custom middleware that logs before and after each request, then forwards it to the route.

Python
from fastapi import FastAPI
from fastapi.requests import Request
app = FastAPI()

# Custom middleware
@app.middleware("http")
async def demo_mw(request: Request, call_next):
    print("Middleware, Before request")
    response = await call_next(request)  # Pass request to the next process
    print("Middleware, After response")
    return response

# A test route
@app.get("/")
def home():
    return {"message": "Hello, FastAPI with Middleware!"}

Output:

simpleCustomMiddleware_output
Output of Example 1

The terminal displays:

Middleware, Before request
Middleware, After response

This shows middleware runs around every request.

Explanation:

  • @app.middleware("http"): Declares a middleware for all HTTP requests.
  • request: Represents incoming request.
  • call_next(request): Forwards request to the actual route (/ in this case).

Example 2: This code defines a logging_middleware that measures request time and logs the method, URL, and duration before returning the response.

Python
import time
from fastapi import FastAPI, Request
app = FastAPI()

@app.middleware("http")
async def logging_middleware(request: Request, call_next):
    start_time = time.time()  # Record start time
    response = await call_next(request)  # Process request
    process_time = time.time() - start_time  # Calculate duration
    print(f"Request: {request.method} {request.url} completed in {process_time:.4f} seconds")
    return response

@app.get("/hello")
def hello():
    return {"message": "Hello World"}

Output:

loggingCustomMiddleware_Output
Output of Example 2

And in the terminal:

Request: GET http://127.0.0.1:8000/hello completed in 0.0009 seconds

It means middleware logged that a GET request was made to /hello and server took 0.0009 seconds to process and return the response.

Explanation:

  • time.time(): Records the current time before the request is processed.
  • request.method: Shows method like GET/POST.
  • request.url: Shows which URL was requested.

Example 3: This code defines a middleware that adds an X-Custom-Header to every response, eliminating the need to set it in each route.

Python
from fastapi import FastAPI, Request
app = FastAPI()

@app.middleware("http")
async def add_custom_header(request: Request, call_next):
    response = await call_next(request)  # process the request
    response.headers["X-Custom-Header"] = "This is a custom header"
    return response

# Sample route
@app.get("/hello")
def say_hello():
    return {"message": "Hello from FastAPI!"}

Output:

AddingHeader_customMiddleware_Output2
Output of Example 3

Open Developer Tools → go to the Network tab → select the /hello request → check the Response Headers section to see the custom header added by the middleware.

AddingHeader_customMiddleware_Output
Output

Explanation:

  • @app.middleware("http"): Defines middleware for all HTTP requests.
  • add_custom_header(): Runs before/after every request.
  • call_next(request): Passes request to the next step (actual route).
  • response.headers[]: Adds a new header to the response.

To understand Middlewares, refer this article: Middlewares in FastAPI

Comment