Spaces:
Running
Dependency Injection in FastAPI
FastAPI includes a built-in dependency injection system that allows you to share logic, enforce authentication, manage database connections, and more. Dependencies are declared using Depends() and are resolved automatically for each request.
Basic Dependency
A dependency is any callable (function or class) that FastAPI calls before the route handler:
from fastapi import FastAPI, Depends, Query
app = FastAPI()
async def common_parameters(
skip: int = Query(default=0, ge=0),
limit: int = Query(default=100, ge=1, le=1000),
):
return {"skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return {"params": commons}
@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
return {"params": commons}
Both /items/ and /users/ share the same pagination logic. The common_parameters function is called once per request, and its return value is injected into the commons parameter.
Class-Based Dependencies
Classes work as dependencies because calling a class creates an instance (i.e., MyClass() is callable):
class PaginationParams:
def __init__(
self,
skip: int = Query(default=0, ge=0),
limit: int = Query(default=100, ge=1, le=1000),
):
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(pagination: PaginationParams = Depends(PaginationParams)):
return {"skip": pagination.skip, "limit": pagination.limit}
FastAPI provides a shorthand: Depends(PaginationParams) can also be written as Depends() when the type annotation already specifies the class: pagination: PaginationParams = Depends().
Sub-Dependencies
Dependencies can depend on other dependencies, forming a chain that FastAPI resolves automatically:
def query_extractor(q: str | None = None):
return q
def query_or_default(q: str = Depends(query_extractor)):
if not q:
return "default_query"
return q
@app.get("/items/")
async def read_items(query: str = Depends(query_or_default)):
return {"query": query}
FastAPI resolves the dependency tree from the leaves up. In this case, query_extractor runs first, then query_or_default receives its result. The maximum depth of the dependency chain is not explicitly limited, but in practice chains deeper than 10 levels indicate a design issue.
Dependencies with Yield (Resource Management)
Use yield in a dependency to run setup code before and cleanup code after the route handler executes. This is ideal for managing database sessions, file handles, or locks:
from sqlalchemy.orm import Session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/items/")
async def read_items(db: Session = Depends(get_db)):
items = db.query(Item).all()
return items
The code before yield runs before the handler, the yielded value is injected as the dependency, and the code after yield runs after the response is sent. The finally block ensures cleanup happens even if an exception occurs. FastAPI supports up to 32 yield dependencies per request by default.
Global Dependencies
Apply dependencies to every route in the application by passing them to the FastAPI constructor:
from fastapi import FastAPI, Depends, Header, HTTPException
async def verify_api_key(x_api_key: str = Header()):
if x_api_key != "secret-key-123":
raise HTTPException(status_code=403, detail="Invalid API key")
app = FastAPI(dependencies=[Depends(verify_api_key)])
@app.get("/items/")
async def read_items():
return [{"item": "Widget"}]
Every route in this application requires a valid X-Api-Key header. You can also scope dependencies to a specific router using APIRouter(dependencies=[...]).
Caching Behavior
By default, if the same dependency is used multiple times within a single request (e.g., both a route and a sub-dependency use Depends(get_db)), FastAPI caches the result and calls the dependency only once. To disable caching and force a fresh call each time, use Depends(get_db, use_cache=False).