Middleware is a piece of code that sits between the web server and the route handlers in a web application. It intercepts incoming requests and outgoing responses, allowing you to perform various tasks such as logging, authentication, request/response modification, and more. Middleware can be used to add cross - cutting concerns to your application without cluttering the route handlers.
FastAPI comes with some built - in middleware that you can easily add to your application. For example, the CORSMiddleware
is used to handle Cross - Origin Resource Sharing (CORS).
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# List of allowed origins
origins = [
"http://localhost",
"http://localhost:8080",
]
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
def read_root():
return {"Hello": "World"}
In this example, we first import the CORSMiddleware
from fastapi.middleware.cors
. Then we define a list of allowed origins and add the middleware to our FastAPI application using the add_middleware
method.
You can also create your own custom middleware. A custom middleware in FastAPI is an asynchronous function that takes a request
object and a call_next
function. The call_next
function is used to pass the request to the next middleware or the route handler.
from fastapi import FastAPI, Request
import time
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
@app.get("/")
def read_root():
return {"Hello": "World"}
In this example, we define a custom middleware using the @app.middleware("http")
decorator. The middleware measures the time it takes to process a request and adds a custom header X - Process - Time
to the response.
You can use middleware to log incoming requests and outgoing responses. This can be useful for debugging and monitoring purposes.
from fastapi import FastAPI, Request
import logging
app = FastAPI()
logging.basicConfig(level=logging.INFO)
@app.middleware("http")
async def log_requests(request: Request, call_next):
logging.info(f"Request: {request.method} {request.url}")
response = await call_next(request)
logging.info(f"Response status code: {response.status_code}")
return response
@app.get("/")
def read_root():
return {"Hello": "World"}
Middleware can be used to perform authentication checks on incoming requests. For example, you can check for the presence of an authentication token in the request headers.
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
@app.middleware("http")
async def authenticate(request: Request, call_next):
token = request.headers.get("Authorization")
if not token:
raise HTTPException(status_code=401, detail="Unauthorized")
# Here you can add more complex authentication logic
response = await call_next(request)
return response
@app.get("/")
def read_root():
return {"Hello": "World"}
Each middleware should have a single responsibility. For example, a logging middleware should only focus on logging, and an authentication middleware should only focus on authentication. This makes the code more maintainable and easier to understand.
The order in which you add middleware to your FastAPI application matters. Middleware is executed in the order they are added. So, if you have an authentication middleware and a logging middleware, you may want to add the authentication middleware first to prevent unnecessary logging of unauthorized requests.
from fastapi import FastAPI, Request, HTTPException
import logging
app = FastAPI()
logging.basicConfig(level=logging.INFO)
@app.middleware("http")
async def authenticate(request: Request, call_next):
token = request.headers.get("Authorization")
if not token:
raise HTTPException(status_code=401, detail="Unauthorized")
response = await call_next(request)
return response
@app.middleware("http")
async def log_requests(request: Request, call_next):
logging.info(f"Request: {request.method} {request.url}")
response = await call_next(request)
logging.info(f"Response status code: {response.status_code}")
return response
@app.get("/")
def read_root():
return {"Hello": "World"}
Make sure to handle errors properly in your middleware. If an error occurs in a middleware, it should either return an appropriate error response or raise an exception that can be handled by the global exception handler in FastAPI.
Middleware is a powerful feature in FastAPI that allows you to add cross - cutting concerns to your application in a modular and efficient way. By understanding how to add built - in and custom middleware, and following common and best practices, you can build more robust and maintainable FastAPI applications. Whether it’s handling CORS, logging, or authentication, middleware provides a flexible way to enhance your application’s functionality.