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.