Skip to content

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:
    ...

Released under the MIT License.