Skip to content

How-to: FastAPI Depends パターン

FastAPI の Depends() の使い方と、Annotated スタイルへの統一パターンを説明する。


1. 2 つの Depends スタイル

FastAPI には Depends() の書き方が 2 通りある。

python
# スタイル A: = Depends(...) (デフォルト値スタイル)
def handler(
    use_case: FetchUserUseCase = Depends(get_fetch_user_use_case),
) -> JSONResponse: ...

# スタイル B: Annotated[T, Depends(...)] (Annotated スタイル)
def handler(
    use_case: Annotated[FetchUserUseCase, Depends(get_fetch_user_use_case)],
) -> JSONResponse: ...

推奨: Annotated スタイル(スタイル B)に統一する


2. スタイル混在で起きる SyntaxError

= Depends(...)Annotated[T, Depends()] を同じ関数に混在させると SyntaxError が発生する。

python
# ❌ SyntaxError: parameter without a default follows parameter with a default
def list_articles(
    filter_: ArticleFilter = Depends(get_filter),   # デフォルト値あり
    pagination: Annotated[PaginationQueryParser, Depends()],  # デフォルト値なし
) -> JSONResponse: ...

# ✅ Annotated スタイルに統一
def list_articles(
    filter_: Annotated[ArticleFilter, Depends(get_filter)],
    pagination: Annotated[PaginationQueryParser, Depends()],
) -> JSONResponse: ...

Python の仕様として、デフォルト値を持つ引数の後にデフォルト値なしの引数は置けない。 Annotated[T, Depends()] はデフォルト値を持たないため、= Depends(...) の後には書けない。


3. PaginationQueryParser の正しい使い方

PaginationQueryParserAnnotated[T, Depends()] で使う。as_depends() は存在しない。

python
from typing import Annotated
from fastapi import Depends
from nene2.http import PaginationQueryParser

# ✅ 正しい
def list_items(
    pagination: Annotated[PaginationQueryParser, Depends()],
) -> JSONResponse:
    result = use_case.execute(pagination.limit, pagination.offset)
    ...

# ❌ as_depends() は存在しない
def list_items(
    pagination: PaginationQueryParser = Depends(PaginationQueryParser.as_depends()),
) -> JSONResponse: ...

4. カスタムフィルター + ページネーション

複数の Depends() パラメーターを組み合わせる場合はすべて Annotated スタイルで統一する。

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_.status, filter_.tag
    # ページネーション: pagination.limit, pagination.offset
    ...

5. Depends でキャッシュを注入する

Annotated スタイルで app.state のキャッシュを注入する例。

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

MIT ライセンスの下でリリースされています。