Initial extracted documentation set

This commit is contained in:
Rodin
2026-06-01 21:42:05 +00:00
commit a23e494026
16 changed files with 1414 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
# 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.