Dependency Patterns Cheatsheet
Quick-lookup reference for FastAPI dependency injection — function dependencies, class dependencies, yield/teardown, security deps, router-level deps, and test overrides.
Learning Focus
By the end of this reference you can: quickly recall the syntax for every common dependency injection pattern in FastAPI.
Basic Patterns
from fastapi import Depends
from typing import Annotated
# Function dependency
async def get_db() -> AsyncSession: ...
DBSession = Annotated[AsyncSession, Depends(get_db)]
# Class dependency (instantiated per-request)
class Pagination:
def __init__(self, page: int = 1, per_page: int = 10):
self.skip = (page - 1) * per_page
self.limit = per_page
PageDep = Annotated[Pagination, Depends(Pagination)]
# Dependency factory (parameterized)
def require_role(role: str):
async def _check(user: CurrentUser) -> UserORM:
if user.role != role:
raise HTTPException(403, "Forbidden")
return user
return Depends(_check)
AdminUser = Annotated[UserORM, require_role("admin")]
Yield Dependencies
async def get_db():
async with AsyncSessionFactory() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
# Teardown always runs (even on exception)
Security Dependencies
from fastapi import Security
from fastapi.security import OAuth2PasswordBearer, APIKeyHeader
oauth2 = OAuth2PasswordBearer(tokenUrl="/auth/token")
api_key_header = APIKeyHeader(name="X-API-Key")
async def get_current_user(
token: Annotated[str, Depends(oauth2)],
) -> UserORM: ...
# Use Security() for scope-aware dependencies
from fastapi.security import SecurityScopes
async def get_user_with_scopes(
scopes: SecurityScopes,
token: Annotated[str, Depends(oauth2)],
) -> UserORM: ...
# In route
@router.get("/items")
async def f(user: Annotated[UserORM, Security(get_user_with_scopes, scopes=["items:read"])]): ...
Router-Level Dependencies
router = APIRouter(
prefix="/admin",
dependencies=[
Depends(get_current_user),
Depends(require_role("admin")),
],
)
Application-Level Dependencies
app = FastAPI(dependencies=[Depends(rate_limit)])
Dependency Overrides in Tests
app.dependency_overrides[get_db] = override_db
app.dependency_overrides[get_current_user] = fake_user
# After tests:
app.dependency_overrides.clear()
# Disable caching (call dep fresh each time)
async def f(x: Annotated[int, Depends(get_x, use_cache=False)]): ...
Common Pitfalls
| Pitfall | Cause / Symptom | Fix |
|---|---|---|
| Dependency called twice per request | use_cache=False set on a shared dependency | Remove use_cache=False unless fresh results are required per-injection |
| Cleanup not running on exception | yield dependency missing try/except | Wrap yield body in try/except/finally for guaranteed teardown |
Depends on a class instance | Using Depends(MyClass()) instead of Depends(MyClass) | Pass the class, not an instance: Depends(MyClass) |
Hands-On Practice
quick-di-test.py
from typing import Annotated
from fastapi import FastAPI, Depends, HTTPException, Header
from pydantic import BaseModel
app = FastAPI()
class Pagination:
def __init__(self, page: int = 1, per_page: int = 10):
self.skip = (page - 1) * per_page
self.limit = per_page
async def verify_api_key(x_api_key: Annotated[str | None, Header()] = None):
if x_api_key != "secret":
raise HTTPException(401, "Invalid API key")
PageDep = Annotated[Pagination, Depends(Pagination)]
KeyDep = Annotated[None, Depends(verify_api_key)]
class Item(BaseModel):
name: str
price: float
ITEMS = [Item(name=f"Item {i}", price=float(i)) for i in range(1, 21)]
@app.get("/items")
async def list_items(pagination: PageDep, _: KeyDep) -> list[Item]:
return ITEMS[pagination.skip : pagination.skip + pagination.limit]
# Run: uvicorn quick-di-test:app --reload
# Test: curl "http://localhost:8000/items?page=2&per_page=5" -H "X-API-Key: secret"