Concept lesson

FastAPI Dependency Injection

Request-scoped dependencies, yield cleanup generators, and test overrides.

lesson
Freshness: current15 min read
Mastery
not started · 0%

Learning outcomes

  • Build hierarchical dependency DAGs
  • Manage connection lifespans and mocks

Mental model

FastAPI's dependency injection engine constructs a Directed Acyclic Graph (DAG) of dependencies per HTTP request, solving requirements, managing resource scope, and closing yield generators upon request completion.

Incoming HTTP Request
Resolve Dependency DAG
Execute Yield Setup
Inject Resource into Endpoint
Teardown Yield Cleanup
Conceptual teaching model synthesized from:FastAPI Framework Architecture & Dependency Injection Specification

Theory

Dependencies declared with Depends(func) can be nested arbitrarily deep. When a dependency uses yield instead of return, FastAPI executes code before yield prior to route handler execution, injects the yielded value, and executes code after yield during response cleanup (even if an exception occurs).

from typing import AsyncGenerator
from fastapi import FastAPI, Depends
from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app: FastAPI):
    app.state.db = {"active": True}
    yield
    app.state.db["active"] = False

app = FastAPI(lifespan=lifespan)

async def get_db_session() -> AsyncGenerator[dict, None]:
    session = {"session_id": "tx_9942", "db": app.state.db}
    try:
        yield session
    finally:
        session["closed"] = True

@app.get("/items")
async def read_items(db=Depends(get_db_session)):
    return {"status": "ok", "session": db["session_id"]}

Alternatives and trade-offs

Manual dependency instantiation leads to code duplication and tight coupling. FastAPI's app.dependency_overrides allows swapping real databases for in-memory mocks during Pytest runs seamlessly.

Failure modes and misconceptions

  1. Leaking resources without finally: Failing to wrap yield cleanup in try...finally leaves database sessions unclosed if route handlers raise uncaught exceptions.
  2. Global state mutation: Modifying global dependency state across concurrent requests causes race conditions.
Reflect before revealing the guide

Decision scenario

Use asynccontextmanager lifespans for global application resource pools (database pools, Redis clients). Use Depends with yield generators for per-request session isolation.

Learning outcomes

  • Build composable dependency DAGs in FastAPI routes.
  • Implement lifespan context managers for application lifecycle state.
  • Swap production dependencies with test doubles via dependency_overrides.

Trade-offs

FastAPI dependency injection simplifies composition and testing, but complex nested dependency graphs can make execution order harder to trace without clear logging.

Evidence assessment

Theory and decision mastery

not-started · 0%
theory0%
decision0%
activityNot mapped
projectNot mapped
1. How does FastAPI handle cleanup for dependencies defined with `yield`?
2. How does FastAPI simplify unit testing with mock databases or credentials?
3. What risk occurs if post-yield cleanup code in a dependency is not wrapped in `try...finally`?

Decision scenario

An engineering team wants to manage global Redis connection pools initialized when the API starts up and closed on shutdown.

Where should global connection pool management be implemented in FastAPI?

Primary sources