Dependencies¶
FastAPI's dependency injection (Depends) builds what an endpoint needs (a database session, the current
user, common parameters) before calling it, caches each dependency for the request, and runs the cleanup code
of yield dependencies afterwards. The binary resolves the same graph, in the same order.
A function dependency shared through an Annotated alias, a dependency required by every route of
a router (dependencies=[...]), a class dependency and a yield dependency whose cleanup runs once the
request is over. Like every example on this site, it is compiled and compared with FastAPI in CI (how).
from typing import Annotated
from fastapi import APIRouter, Depends, Header, HTTPException
async def verify_key(x_key: Annotated[str, Header()]):
if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
# every route of this router requires the X-Key header
router = APIRouter(prefix="/dependencies", tags=["dependencies"], dependencies=[Depends(verify_key)])
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
CommonsDep = Annotated[dict, Depends(common_parameters)]
@router.get("/items")
async def read_items(commons: CommonsDep):
return commons
class Pagination:
def __init__(self, skip: int = 0, limit: int = 3):
self.skip = skip
self.limit = limit
FRUITS = ["apple", "banana", "cherry", "date", "elderberry", "fig", "grape"]
@router.get("/fruits")
async def read_fruits(page: Annotated[Pagination, Depends(Pagination)]):
return FRUITS[page.skip : page.skip + page.limit]
CLOSED: list[str] = []
async def get_connection():
connection = {"name": f"connection-{len(CLOSED) + 1}"}
try:
yield connection
finally:
CLOSED.append(connection["name"]) # runs once the request is over
@router.get("/connection")
async def use_connection(connection: Annotated[dict, Depends(get_connection)]):
return {"using": connection["name"], "closed_so_far": CLOSED}
Left to Python in this example
GET /dependencies/fruits depends on a plain class (Depends(Pagination), or Depends() with the class
as annotation): py2axum only accepts project functions, Pydantic models (Depends()) and the session
dependency there, so py2axum check reports it and --python-side auto relays it. Write the dependency
as a function returning the object to keep it native.
What is native¶
Depends(...)with project functions, sub-dependencies, the per-request cache,Annotated[T, Depends(...)]aliases,dependencies=[...]on routes and routers.p: Model = Depends()with a Pydantic model (FastAPI calls the class): one query parameter per field, with its type, default andFieldconstraints (ge,le,max_length,pattern...), thenModel(**fields).yielddependencies: oneyield, in the body or in a top-leveltry(except,else,finally). The code afteryieldruns at the end of the request, most recent dependency first, before the session commit. An exception of the request (the endpoint's, a later dependency's, a validation error) is raised at theyield, as FastAPI does: anexceptclause sees it, what it raises replaces it (anHTTPExceptionbecomes the response), and an exception caught and not raised again is FastAPI'sFastAPIError("Response not awaited...", a 500).Depends(dep, scope="function")(FastAPI ≥ 0.121): the exit code runs when the endpoint returns, before the response is sent, and an exception there is the request's; the default scope,"request", runs it once the response is sent (for a streamed response, at the end of the stream). The cache key includes the scope.- An instance of a project class with
__call__as a dependency (checker = Checker("bar"),Depends(checker)): its parameters are those of__call__, sync or async. app.dependency_overrides[dep] = otherwritten at module level (a project function overridden by a project function):otheris solved and called whereverdepis depended on, with its own parameters.- The session dependency:
async with maker() as s: yield swith optionalawait s.commit(),except: await s.rollback(); raise,finally: await s.close(); theasync_sessionmakeroptions (expire_on_commit,autoflush) are read from it, or from a project class wrapping it. The commit afteryieldruns after the endpoint, like FastAPI ≥ 0.121 (a failure is only logged); the binary commits just before sending a complete body (FastAPI just after): same response, without the read-after-write race.
Synchronous session dependencies are described in SQL databases, security schemes used as dependencies in Security.
What stays in Python¶
- A plain class as a dependency (
Depends(MyClass), orDepends()with the class as annotation) is refused: only project functions, Pydantic models and the session dependency are accepted. p: Model = Depends(): a base other thanBaseModel, validators or other decorated methods,model_config, aliases, container fields (FastAPI reads those from the body) are refused.- A request-scoped
yielddependency depending on ascope="function"one is refused (FastAPI'sDependencyScopeError), as is a non-literalscope=. app.dependency_overrideswith anything else than a project function on both sides, or changed anywhere else than at module level, is refused (an override set by a test, in-process, does not reach the binary).