FastAPI Dependency Injection DAG & Lifespan Context
FastAPI features a powerful Dependency Injection (DI) system. Unlike spring-like XML DI frameworks, FastAPI’s DI engine resolves dependencies dynamically per request, constructing a Directed Acyclic Graph (DAG) of sub-dependencies, resolving database connections, and managing resource cleanups using generator yield patterns.
This chapter details FastAPI’s Dependency DAG resolution engine, yield context cleanups, sub-dependencies, and modern ASGI lifespan event context management.
1. Dependency Injection DAG Engine (Depends())
FastAPI’s dependency engine (fastapi.dependencies.utils.solve_dependencies) analyzes path operation functions during request handling:
- DAG Tree Traversal: Constructs a tree of required dependencies.
- Sub-Dependency Resolution: If Dependency A depends on Dependency B (
Depends(get_db)), FastAPI resolves Dependency B first. - Dependency Caching (
use_cache=True): By default, if multiple dependencies in the same request tree requestDepends(get_db), FastAPI executesget_dbonce and caches the result for the lifetime of that specific HTTP request.
FastAPI Dependency Injection DAG:
HTTP Request Arrives
|
v
[ Path Operation: get_user_orders(user=Depends(get_current_user), db=Depends(get_db)) ]
|
+---> Resolve 1: get_db() -> Opens DB Session (Cached for request)
|
+---> Resolve 2: get_current_user(token=Depends(oauth2_scheme), db=Depends(get_db))
| |
| └---> Reuses Cached db Session! (use_cache=True)
v
Decodes JWT & Queries DB
|
v
[ Execute Path Operation Handler ]
|
v
[ Cleanup Phase: Execute post-yield code in reverse DAG order ]2. Context Cleanup via yield Dependencies
Dependencies can manage stateful resources (database sessions, HTTP client connections, locks) using a generator function with yield:
from typing import Generator
from sqlalchemy.orm import Session
from myapp.database import SessionLocal
def get_db() -> Generator[Session, None, None]:
db = SessionLocal()
try:
yield db # Provide resource to route handler
finally:
db.close() # GUARANTEED CLEANUP after request finishes!Execution Flow:
- Code before
yieldexecutes prior to running the view handler. - The yielded value is injected into the view parameter.
- Code after
yield(or insidefinally:) executes after the response has been sent to the client.
3. ASGI Lifespan Events (asynccontextmanager)
Legacy FastAPI used @app.on_event("startup") and @app.on_event("shutdown"). Modern FastAPI uses the ASGI Lifespan interface (asynccontextmanager), which encapsulates application-wide setup and teardown inside a single context manager:
from contextlib import asynccontextmanager
from fastapi import FastAPI
import httpx
# Shared application state
state = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
# STARTUP: Initialize database connection pools & HTTP clients
state["http_client"] = httpx.AsyncClient()
yield
# SHUTDOWN: Gracefully close pools & clients
await state["http_client"].aclose()
app = FastAPI(lifespan=lifespan)4. Production Trade-offs & Dependency Scope
use_cache=True(Default): Shares the dependency result within a single HTTP request tree.use_cache=False: Forces FastAPI to re-evaluate the dependency every time it is referenced in the dependency tree. Useuse_cache=Falsewhen distinct sub-components require independent, un-shared resource instances.