Technical
FastAPI Dependency Injection: Patterns That Survive Refactors
FastAPI's dependency injection is one of the best parts of the framework and one of the easiest to abuse. After months of working on FastAPI services that I didn't originally write, I've seen both sides. Here's what holds up when the code has to be refactored in six months.
The Good Pattern
A dependency is a function that produces something the handler needs. The handler declares what it needs. The framework wires it up. Tests override.
from fastapi import Depends, FastAPI
def get_db():
with SessionLocal() as session:
yield session
def get_current_user(token: str = Depends(oauth_scheme), db = Depends(get_db)):
return db.get(User, decode(token).sub)
@app.get('/me')
def me(user: User = Depends(get_current_user)):
return userThe handler just takes a User. Where the User comes from is someone else's problem. That's the superpower.
The Abuse I Keep Fixing
People reach for Depends when a plain import would do. If the dependency has no per-request state, it's a singleton. Import it. Don't wrap it in a dependency function just because you can.
The Test Payoff
The reason to use Depends at all is this one line:
app.dependency_overrides[get_current_user] = lambda: fake_userEvery handler that depends on the current user now gets the fake. No mocking libraries, no patch decorators. That simplicity is why the pattern survives.
Full docs at FastAPI dependencies.
RELATED READING
The Consulting Shift I Am Making In Year Two
After a year of writing and building, my consulting practice is changing shape. Shorter engagements. Sharper outcomes.
ReadThe Frontend Shift: Shipping Less JavaScript In Year Two
A year ago I reached for Next.js for everything. This year I often reach for nothing.
ReadThe Serverless Lesson I Would Write On A Sticky Note
After a year of shipping serverless projects, one rule explains most of the wins and all of the losses.
Read