How-to: FastAPI Depends patterns
How to use FastAPI's Depends(), and the convention of standardizing on the Annotated style.
1. Two Depends styles
FastAPI has two ways to write Depends().
python
# Style A: = Depends(...) (default-value style)
def handler(
use_case: FetchUserUseCase = Depends(get_fetch_user_use_case),
) -> JSONResponse: ...
# Style B: Annotated[T, Depends(...)] (Annotated style)
def handler(
use_case: Annotated[FetchUserUseCase, Depends(get_fetch_user_use_case)],
) -> JSONResponse: ...Recommended: standardize on the Annotated style (Style B).
2. SyntaxError from mixing styles
Mixing = Depends(...) and Annotated[T, Depends()] in the same function raises a SyntaxError.
python
# ❌ SyntaxError: parameter without a default follows parameter with a default
def list_articles(
filter_: ArticleFilter = Depends(get_filter), # has a default
pagination: Annotated[PaginationQueryParser, Depends()], # no default
) -> JSONResponse: ...
# ✅ standardize on the Annotated style
def list_articles(
filter_: Annotated[ArticleFilter, Depends(get_filter)],
pagination: Annotated[PaginationQueryParser, Depends()],
) -> JSONResponse: ...As a Python rule, a parameter without a default cannot follow one with a default. Annotated[T, Depends()] has no default, so it cannot come after = Depends(...).
3. The correct way to use PaginationQueryParser
Use PaginationQueryParser with Annotated[T, Depends()]. There is no as_depends().
python
from typing import Annotated
from fastapi import Depends
from nene2.http import PaginationQueryParser
# ✅ correct
def list_items(
pagination: Annotated[PaginationQueryParser, Depends()],
) -> JSONResponse:
result = use_case.execute(pagination.limit, pagination.offset)
...
# ❌ as_depends() does not exist
def list_items(
pagination: PaginationQueryParser = Depends(PaginationQueryParser.as_depends()),
) -> JSONResponse: ...4. Custom filter + pagination
When combining multiple Depends() parameters, standardize all of them on the Annotated style.
python
from dataclasses import dataclass
from enum import StrEnum
from typing import Annotated
from fastapi import Depends, FastAPI, Query
from fastapi.responses import JSONResponse
from nene2.http import PaginationQueryParser
class Status(StrEnum):
ACTIVE = "active"
INACTIVE = "inactive"
@dataclass(frozen=True, slots=True)
class ItemFilter:
status: Status | None
tag: str | None
def get_item_filter(
status: Annotated[Status | None, Query()] = None,
tag: Annotated[str | None, Query(max_length=50)] = None,
) -> ItemFilter:
return ItemFilter(status=status, tag=tag)
@app.get("/items")
def list_items(
filter_: Annotated[ItemFilter, Depends(get_item_filter)],
pagination: Annotated[PaginationQueryParser, Depends()],
) -> JSONResponse:
# filter: filter_.status, filter_.tag
# pagination: pagination.limit, pagination.offset
...5. Injecting a cache via Depends
An example of injecting an app.state cache in the Annotated style.
python
from fastapi import Request
from nene2.cache import TtlCache
def get_cache(request: Request) -> TtlCache[dict[str, object]]:
return request.app.state.cache # type: ignore[attr-defined]
@app.get("/items/{item_id}")
def get_item(
item_id: int,
cache: Annotated[TtlCache[dict[str, object]], Depends(get_cache)],
) -> JSONResponse:
...