Depends — Dependency Injection Basics
IntermediateDepends(fn) tells FastAPI: run fn first (resolving ITS parameters the same way) and hand me the result. Shared logic — pagination, auth, DB sessions — becomes declared-once, injected-everywhere.
Overview
Dependency injection is FastAPI's signature feature and the reason large codebases stay clean. A dependency is any callable; declaring a parameter as Depends(get_thing) makes FastAPI call it before your endpoint — and because dependency parameters are resolved with the exact same rules (query, path, body, or further Depends), dependencies compose into trees. The classic first use is shared pagination parameters: declare page/size validation once, inject into twenty list endpoints. Within a single request, a dependency used in multiple places runs once and its result is cached — so five things asking for the current user trigger one lookup. This chapter is the mechanism; the next two are the patterns built on it.
The Mechanism — Callables Resolved Before You
A dependency's own parameters bind from the request (query params here). The endpoint receives the return value. Twenty list endpoints, one definition of pagination — change the max page size in one place.
from typing import Annotated
from fastapi import Depends, FastAPI, Query
app = FastAPI()
# A dependency is just a function — its params bind like any endpoint's
def pagination(
page: int = Query(default=1, ge=1),
size: int = Query(default=20, ge=1, le=100),
) -> dict:
return {"offset": (page - 1) * size, "limit": size}
# Annotated alias — define once, reuse as a type (modern style)
Pagination = Annotated[dict, Depends(pagination)]
@app.get("/students")
def list_students(p: Pagination):
return {"slice": f"students[{p['offset']}:{p['offset'] + p['limit']}]"}
@app.get("/drives")
def list_drives(p: Pagination): # same rules, zero duplication
return {"slice": f"drives[{p['offset']}:{p['offset'] + p['limit']}]"}
# GET /students?page=3&size=50 → {"slice": "students[100:150]"}
# GET /students?size=500 → 422 (le=100) — enforced EVERYWHERE at once
# /docs shows page & size on both endpoints — dependencies are documented tooSub-Dependencies and Per-Request Caching
Dependencies can depend on dependencies — FastAPI resolves the tree. Within one request each node runs once (result cached), so a chain that needs the current user three times hits the lookup once.
from typing import Annotated
from fastapi import Depends, FastAPI, Header, HTTPException
app = FastAPI()
CALLS = {"user_lookups": 0}
def get_token(authorization: str | None = Header(default=None)) -> str:
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(401, "missing bearer token")
return authorization.removeprefix("Bearer ")
def get_current_user(token: Annotated[str, Depends(get_token)]) -> dict:
CALLS["user_lookups"] += 1 # prove the caching
return {"id": 7, "name": "Asha", "role": "tpo"} # (JWT decode → Auth chapter)
def get_user_college(user: Annotated[dict, Depends(get_current_user)]) -> str:
return "COEP Pune"
@app.get("/dashboard")
def dashboard(
user: Annotated[dict, Depends(get_current_user)], # needs user
college: Annotated[str, Depends(get_user_college)], # ALSO needs user
):
return {"user": user["name"], "college": college,
"lookups_this_request": CALLS["user_lookups"]}
# → lookups_this_request: 1 — get_current_user ran ONCE, result reused
# (opt out per dependency with Depends(fn, use_cache=False))
# The tree FastAPI resolved:
# dashboard ── get_current_user ── get_token ── Header
# └── get_user_college ── get_current_user (cache hit)Key Points to Remember
- 1Depends(fn): fn runs first, its params bind from the request, you get the result
- 2Annotated[T, Depends(fn)] aliases make dependencies reusable one-liners
- 3Dependencies nest into trees; FastAPI resolves the whole graph
- 4Per-request caching: the same dependency runs once per request by default
Interview Questions
Sign in to ask AriaExplain FastAPI's Depends to someone who knows Spring — what maps to what?
Five dependencies in one request all need the current user — how many DB lookups happen and why?
When would you pass use_cache=False to a dependency?
Ask Aria about Depends — Dependency Injection Basics
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.