Day 11 · ~12m

Dependencies & Middleware

Use Depends() for reusable logic, shared state, and request preprocessing.

🧑‍💻

I keep writing the same validation logic in multiple endpoints. Is there a way to reuse it?

👩‍🏫

That's exactly what dependencies solve. FastAPI's Depends() lets you extract shared logic into reusable functions:

from fastapi import FastAPI, Depends, HTTPException

app = FastAPI()

def get_api_key(api_key: str):
    if api_key != "secret-key-123":
        raise HTTPException(status_code=401, detail="Invalid API key")
    return api_key

@app.get("/protected")
def protected_route(key: str = Depends(get_api_key)):
    return {"message": "You're in!", "key": key}

Depends(get_api_key) tells FastAPI: "Before running this endpoint, call get_api_key first. If it raises an exception, stop. If it returns a value, pass that value as key." The dependency runs before every request to this endpoint.

🧑‍💻

Can a dependency use query parameters or headers?

👩‍🏫

Absolutely. Dependencies are just functions — they can take the same parameters as endpoints:

from fastapi import Header

def verify_token(authorization: str = Header()):
    if not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Invalid token format")
    token = authorization.replace("Bearer ", "")
    return {"token": token, "user_id": 42}  # In reality, decode the token

@app.get("/me")
def get_me(auth: dict = Depends(verify_token)):
    return {"user_id": auth["user_id"]}

FastAPI resolves the Header() parameter from the request headers, passes it to verify_token, then passes the result to your endpoint.

🧑‍💻

What about logic that should run on every request — like logging?

👩‍🏫

Use middleware. It wraps every request and response:

import time
from fastapi import Request

@app.middleware("http")
async def log_requests(request: Request, call_next):
    start = time.time()
    response = await call_next(request)
    duration = time.time() - start
    print(f"{request.method} {request.url.path} - {duration:.3f}s")
    return response

This middleware times every request. call_next(request) passes the request to the actual endpoint, and you can inspect or modify the response before returning it.

🧑‍💻

When should I use a dependency vs middleware?

👩‍🏫

Dependencies for endpoint-specific logic: auth, pagination, database connections. They're explicit — you declare them per endpoint.

Middleware for cross-cutting concerns: logging, timing, CORS headers. They run on every request without being declared on individual endpoints.

Think of dependencies as tools you pick up for specific jobs. Middleware is the air in the room — always there.

Practice your skills

Sign up to write and run code in this lesson.

Already have an account? Sign in