FastAPI's dependency injection system lets you declare things your endpoint needs (DB sessions, auth users, pagination, clients) as function parameters. FastAPI builds them per-request and caches them within that request.
from fastapi import FastAPI, Depends, HTTPException
app = FastAPI()
async def pagination(skip: int = 0, limit: int = 20):
return {"skip": skip, "limit": limit}
@app.get("/items/")
def list_items(page: dict = Depends(pagination)):
return {"page": page}
Anything you can declare in an endpoint (query params, headers, bodies) can be declared inside a dependency instead. The client still sends ?skip=10&limit=5; the dependency receives them and the endpoint gets the result.
from fastapi import Depends, HTTPException, Header
def verify_token(x_token: str = Header()):
if x_token != "secret":
raise HTTPException(status_code=401, detail="Invalid token")
return x_token
@app.get("/admin/")
def admin_panel(token: str = Depends(verify_token)):
return {"access": "granted", "token": token}
Raising HTTPException inside a dependency aborts the request before the endpoint runs — dependencies are the natural place for authentication, authorization and rate limiting.
def get_db():
db = SessionLocal()
try:
yield db # endpoint runs with this value
finally:
db.close() # always runs afterwards (like a context manager)
The code before yield is setup, after is teardown. Perfect for DB sessions, transactions and temp files. (Supported directly in FastAPI; nested yield dependencies work too.)
def get_current_user(token: str = Depends(verify_token), db=Depends(get_db)):
...
@app.get("/me/")
def me(user=Depends(get_current_user)):
...
get_db, the session is created once per request and shared.Depends(fn, use_cache=False).class CommonParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 20):
self.q, self.skip, self.limit = q, skip, limit
@app.get("/things/")
def list_things(common: CommonParams = Depends()):
return {"q": common.q, "skip": common.skip}
Depends() with no argument uses the declared class/callable itself. Classes are great when a dependency needs several related values or configuration.
A: Once per request, before the endpoint, resolved lazily as the dependency tree is built. Results are cached for the duration of that single request only — they are not singletons; use module-level objects (engine, connection pools) for cross-request state.
A: Yes: router = APIRouter(dependencies=[Depends(verify_token)]) runs the check on every route but discards the result. Also available per-endpoint: @app.get("/", dependencies=[...]).
A: Middleware runs for every request globally and has no access to route parameters — use it for cross-cutting concerns (CORS, timing, logging). Dependencies are declared per-route (or per-router), can be async, can raise HTTP errors and receive validated parameters — use them for auth, pagination, DB sessions.
Depends is FastAPI's superpower: compose reusable logic (auth, DB, pagination) from small functions, get per-request caching and automatic cleanup with yield, and keep endpoints focused on business logic.