Skip to main content

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

PitfallCause / SymptomFix
Dependency called twice per requestuse_cache=False set on a shared dependencyRemove use_cache=False unless fresh results are required per-injection
Cleanup not running on exceptionyield dependency missing try/exceptWrap yield body in try/except/finally for guaranteed teardown
Depends on a class instanceUsing 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"

What's Next