Dependency Injection
Understand the power of dependency injection in FastAPI for managing dependencies in your applications.
Content
Introduction to Dependencies
Versions:
Watch & Learn
AI-discovered learning video
Sign in to watch the learning video for this topic.
Introduction to Dependencies — FastAPI's Delightfully Practical DI
"Dependency Injection, but without the corporate buzzwords and with more coffee." ☕️
You already know how FastAPI takes your request, validates data, and returns beautifully serialized responses (remember our adventures through Request and Response Handling: cookies, sessions, and streaming?). Dependencies are the next logical stop: they let you compose reusable pieces of logic (auth, DB sessions, feature switches, etc.) and inject them into path operations cleanly — like giving your endpoints exactly the tools they need without hand-delivering them every time.
What is a Dependency in FastAPI (in plain human)
- A dependency is just a callable — a function, async function, or class — that FastAPI runs for you and provides the return value to other functions.
- You declare you need that value by adding a parameter with
Depends(...).
Think of dependencies as the ingredients prepped by an assistant in a kitchen. Your endpoint says "I need chopped onions" and FastAPI runs the assistant (dependency), gives you the onions, and keeps things tidy.
Why use dependencies? (Besides feeling fancy)
- DRY: centralize repeated logic (auth checks, DB session creation, common query parsing).
- Testable: swap implementations easily with
dependency_overrides. - Composable: dependencies can depend on dependencies (sub-dependencies).
- Scoped: executed per-request and cached within the request, so you can call them multiple times without repeating work.
Quick mental question: If you already wrote a function to get a DB session, why not just call it? Dependencies handle wiring, lifecycle, and caching for you — and they keep your path code focused.
Basic example: Hello Depends
from fastapi import FastAPI, Depends
app = FastAPI()
async def common_parameters(q: str | None = None, page: int = 1):
return { 'q': q, 'page': page }
@app.get('/items/')
async def read_items(commons = Depends(common_parameters)):
return commons
Here, read_items doesn't parse query params. common_parameters does it, and FastAPI injects the returned dict into commons.
Real-world dependency patterns (with tasty examples)
1) Authentication: current user
from fastapi import HTTPException, status, Depends
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = verify_token(token)
if not user:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
return user
@app.get('/me')
async def read_me(current_user = Depends(get_current_user)):
return { 'username': current_user.username }
This isolates auth logic. Your endpoints ask for the current user and trust FastAPI to fetch/validate it.
2) DB session with cleanup (the yield pattern)
from sqlalchemy.orm import Session
from fastapi import Depends
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get('/items')
async def read_items(db: Session = Depends(get_db)):
return db.query(Item).all()
Note: yield allows setup and guaranteed teardown. Beautifully deterministic.
3) Dependency caching (executed once per request)
If two dependencies request the same sub-dependency, FastAPI runs the sub-dependency only once per request and reuses the result. This is great for database sessions or expensive setup.
def get_config():
print('loading config')
return { 'feature_x': True }
@app.get('/a')
async def a(cfg = Depends(get_config)):
return cfg
@app.get('/b')
async def b(cfg = Depends(get_config)):
return cfg
During a single request that needs get_config, 'loading config' prints once.
Advanced / Useful Patterns
- Sub-dependencies: a dependency can declare its own
Depends. Great for layering: token -> user -> permissions. - Class-based dependencies: Use
__call__on a class to maintain state or configuration. - APIRouter/app-level dependencies: Declare dependencies in
APIRouterso all routes inherit them (e.g., requiring auth across an admin router). - Override for tests: Use
app.dependency_overridesto replace slow or external dependencies with mocks.
Table: quick comparison
| Use case | How to declare | Notes |
|---|---|---|
| Per-endpoint reuse | function + Depends | Cleanest for small pieces |
| App-wide logic | app/decorator dependencies | Good for global headers or metrics |
| Lifecycle resources | yield dependencies | Ensure cleanup (DB close) |
| Testing | dependency_overrides | Swap easily during tests |
Common confusions (and why people get stuck)
"Is FastAPI doing classical IoC like Java frameworks?"
Nope. FastAPI's DI is lightweight and function-oriented. It’s not a full-blown IoC container with scopes beyond the request lifecycle. It's pragmatic, not mystical.
"Can I use dependencies for startup/shutdown tasks?"
Not for global lifecycle; use @app.on_event('startup') / shutdown. Dependencies are for request-time wiring.
"What about performance?"
Dependencies are executed per request but cached within that request. Keep very expensive global initialization outside dependencies (e.g., create expensive clients on startup and inject via dependency that returns the already-created object).
Hands-on: Testing override example
# in tests
def fake_get_db():
db = FakeSession()
try:
yield db
finally:
db.close()
app.dependency_overrides[get_db] = fake_get_db
def test_read_items():
client = TestClient(app)
response = client.get('/items')
assert response.status_code == 200
This makes your tests deterministic and fast.
Quick checklist: When to make something a dependency
- Is it reused across endpoints? Make it a dependency.
- Does it need setup or teardown? Use yield.
- Will tests benefit from swapping it? Yes -> dependency_overrides.
- Is it global app init? Use startup events instead.
Final words — TL;DR + Mic Drop
- Dependencies are your reusable, testable, composable helpers. They're functions that FastAPI runs for you and then hands their result to endpoints.
- Use them to centralize auth, DB sessions, common parameter parsing, and to keep endpoints tidy like a Marie Kondo-ed codebase.
Quote to remember:
"Good dependencies make endpoints read like tiny novels: focused, crisp, and impossible to skim without learning something important."
Now go refactor that handshake code into a crisp dependency and feel slightly smug while your tests run faster.
Comments (0)
Please sign in to leave a comment.
No comments yet. Be the first to comment!