Cheatsheet for DI. Long-form: Textbook Ch 5 .
Basic
from fastapi import Depends
def get_settings():
return Settings()
@app.get("/info")
async def info(s: Settings = Depends(get_settings)):
return {"db": s.database_url}
Yield (setup + teardown)
async def get_db():
async with AsyncSessionLocal() as session:
yield session # cleanup runs after handler
@app.get("/users")
async def list_(db: AsyncSession = Depends(get_db)):
return await db.execute(select(User))
Sub-dependencies
async def get_settings(): ...
async def get_db(s: Settings = Depends(get_settings)) -> AsyncSession:
...
# Both deps below resolve get_settings ONCE (cached per request)
async def get_user(s: Settings = Depends(get_settings)): ...
Disable caching: Depends(fn, use_cache=False).
Class deps
class Pagination:
def __init__(self, page: int = 1, limit: int = Query(20, le=100)):
self.page, self.limit = page, limit
@app.get("/posts")
async def posts(p: Pagination = Depends()): # () or =Depends(Pagination)
...
Security deps (auto-OpenAPI)
from fastapi.security import OAuth2PasswordBearer, HTTPBearer, APIKeyHeader
oauth2 = OAuth2PasswordBearer(tokenUrl="token")
bearer = HTTPBearer()
api_key = APIKeyHeader(name="X-API-Key")
async def current_user(token: str = Depends(oauth2)) -> User:
return verify(token)
@app.get("/me")
async def me(u: User = Depends(current_user)): ...
Path / router / app-level dependencies
@app.get("/items", dependencies=[Depends(verify_api_key)]) # path-level
async def items(): return [...]
router = APIRouter(dependencies=[Depends(verify_api_key)]) # router
app.include_router(router, dependencies=[Depends(rate_limit)]) # include
app = FastAPI(dependencies=[Depends(global_logger)]) # app
Dependencies stack: app → include → router → path.
Lifespan (app-scoped resources)
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.engine = create_async_engine(URL)
app.state.sm = async_sessionmaker(app.state.engine, expire_on_commit=False)
app.state.http = httpx.AsyncClient(timeout=10)
yield
await app.state.http.aclose()
await app.state.engine.dispose()
app = FastAPI(lifespan=lifespan)
# Access via Request.app.state
async def get_db(request: Request) -> AsyncSession:
async with request.app.state.sm() as session:
yield session
Settings as a dep (cached)
from functools import lru_cache
@lru_cache
def get_settings() -> Settings:
return Settings()
Service pattern (constructor injection inside, DI at boundary)
class UserService:
def __init__(self, db: AsyncSession, mailer: Mailer):
self.db, self.mailer = db, mailer
async def create(self, data): ...
async def get_user_service(
db: AsyncSession = Depends(get_db),
mailer: Mailer = Depends(get_mailer),
) -> UserService:
return UserService(db, mailer)
@app.post("/users")
async def create_(d: UserCreate, svc: UserService = Depends(get_user_service)):
return await svc.create(d)
Test overrides
def override_db(): return TestDB()
app.dependency_overrides[get_db] = override_db
# ...run tests...
app.dependency_overrides = {} # reset
Tenant-scoped session
async def get_tenant(req: Request) -> Tenant:
tid = req.headers.get("x-tenant-id")
return await load_tenant(tid)
async def get_tenant_db(t: Tenant = Depends(get_tenant)) -> AsyncSession:
async with sm() as s:
await s.execute(text("SET LOCAL app.tenant_id = :t"), {"t": t.id})
yield s
Background work (deferred to after response)
from fastapi import BackgroundTasks
@app.post("/signup")
async def signup(u: UserIn, bg: BackgroundTasks, db = Depends(get_db)):
user = await create_user(db, u)
bg.add_task(send_welcome, user.email) # fire-and-forget
return {"id": user.id}
Read this next
If you want my Depends + service + lifespan starter, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .