# Dependencies Use FastAPI dependencies for request-scoped wiring: sessions, auth, permission checks, and other collaborators that should be assembled by the framework before the handler runs. ## Why this convention exists Dependencies are FastAPI's main request-time composition tool. They let you: - keep wiring visible in the handler signature - reuse auth and resource access across routes - give tests a clean override seam - manage `yield`-based resources with framework-controlled lifetime That is better than opening sessions, parsing tokens, or repeating permission logic inside every route body. ## The convention 1. Put reusable request wiring in dependency callables. 2. Use `Annotated[..., Depends(...)]` aliases when they make signatures easier to read. 3. Keep handlers consuming already-shaped collaborators. 4. Raise semantic HTTP errors inside dependencies when the failure is inherently about the request or caller. 5. Use lifespan for app-wide startup resources; use dependencies for per-request access to them. ## Follow it when the concern is request-scoped Typical fits: - DB/session access - current-user lookup - permission gates - token/header parsing - reusable request validation ## Do not over-abstract it Do *not* create a dependency just because a helper exists. Skip dependency injection when: - the logic is local to one route and trivial - the helper is plain business logic with no FastAPI concerns - the abstraction makes the signature harder to understand than the inline code would Also avoid turning the dependency graph into a maze. If a reader has to chase six `Depends(...)` calls just to find the session, the abstraction is working against you. ## Preferred shape ```python from typing import Annotated from fastapi import Depends, HTTPException def get_db() -> Generator[Session, None, None]: with Session(engine) as session: yield session SessionDep = Annotated[Session, Depends(get_db)] def get_current_user(session: SessionDep, token: TokenDep) -> User: user = load_user_from_token(session, token) if not user: raise HTTPException(status_code=404, detail="User not found") return user CurrentUser = Annotated[User, Depends(get_current_user)] @router.get("/items/{id}") def read_item(session: SessionDep, current_user: CurrentUser, id: UUID) -> ItemPublic: ... ``` Why this works: - the route tells you exactly what it needs - auth and session logic are reusable - tests can swap dependencies without patching route internals ## Counterexamples ### Hidden wiring inside handlers ```python @router.get("/items/{id}") def read_item(id: UUID): session = Session(engine) token = parse_auth_header_somehow() user = load_user(session, token) ... ``` Bad because resource and auth wiring are duplicated and easy to get inconsistently wrong. ### Business logic buried in dependencies ```python def get_processed_report(...) -> Report: # loads DB, runs domain rules, emails people, mutates state, etc. ``` Bad because dependencies should shape request collaborators, not become a second invisible service layer. ### Startup work disguised as a dependency If initialization is app-wide and long-lived, it belongs in lifespan, not in a request dependency that quietly performs setup. ## Source signals ### FastAPI - `docs_src/bigger_applications/app_an_py310/dependencies.py:6-13` uses a dedicated dependency callable for request validation and raises an HTTP error there. - `fastapi/routing.py:95-136` wraps request handling in `AsyncExitStack`, which is the mechanism FastAPI uses to manage dependency lifetime, including `yield`-based dependencies. - `tests/test_dependency_security_overrides.py:24-29` defines a route whose collaborators come from `Security(...)` and `Depends(...)` rather than inline setup. - `tests/test_dependency_security_overrides.py:44-63` replaces those collaborators through `app.dependency_overrides`, which is the testing seam FastAPI expects you to use. ### Full-stack FastAPI Template - `backend/app/api/deps.py:21-27` defines a yielded DB session dependency and exposes it as `SessionDep`. - `backend/app/api/deps.py:30-57` decodes auth, loads the user, and raises semantic HTTP errors in a reusable current-user dependency. - `backend/app/api/routes/items.py:13-16` shows the result in practice: the route signature receives `SessionDep` and `CurrentUser` directly. ### Starlette and FastAPI Users - `starlette/applications.py:46-48` says lifespan is the preferred replacement for `on_startup` and `on_shutdown`, reinforcing the split between app-wide resource setup and request-time dependencies. - `examples/beanie-oauth/app/app.py:17-28` initializes Beanie in lifespan. - `examples/beanie-oauth/app/app.py:60-62` protects a route with `Depends(current_active_user)` instead of doing auth lookup inline. ## Bottom line Dependencies should make request wiring more obvious, more reusable, and easier to test. If a dependency hides core business behavior, it is probably the wrong abstraction.