0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Litestar で OpenAPI タグを効率的に管理する方法

0
Posted at

Litestar で OpenAPI タグを効率的に管理する方法

Litestar で API を構築する際、OpenAPI ドキュメント生成のためにタグを定義する場面が多くあります。しかし、タグの定義方法について「どこに書くべきか」「どう一元管理するか」という疑問が生じることがあります。

本記事では、Litestar におけるタグ管理の私的ベストプラクティスを紹介します。

Litestar のタグの役割は2つに分かれている

Litestar では、タグに関して以下の2つの役割があります。

  • ルート側の tags: そのエンドポイント(operation)がどのタグに属するかを示す
  • OpenAPIConfig の tags: タグ自体のメタデータ(説明文、外部ドキュメントへのリンクなど)を定義

つまり、同じタグ名を「API側」と「OpenAPIConfig側」の両方で書く場面があるということです。

実務的な考え方

この2つの役割を整理すると、以下のように考えるのが最も分かりやすいです。

  • タグの所属は各 handler / controller / router 側で付ける
  • タグの説明文や外部ドキュメントは OpenAPIConfig.tags にまとめる
  • タグ名の文字列をバラ撒かないために StrEnum を single source of truth にするのがベストプラクティス

ベストプラクティス

1. タグ名は StrEnum で一元管理する

まず、タグ名を StrEnum で定義します。これにより、タグ名の typo を防ぎ、IDE の補完機能を活用できます。

from enum import StrEnum

class ApiTag(StrEnum):
    USERS = "users"
    ITEMS = "items"
    INTERNAL = "internal"

このエニュムを route 側でも OpenAPI 側でも使用します。

2. ルート側では value を使って紐付ける

Litestar のルート、コントローラー、アプリケーション側の tags は文字列のリストです。明示的に .value を使うことで、API 定義として最終的に文字列になることを保証します。

from litestar import get

@get("/users", tags=[ApiTag.USERS.value])
async def list_users() -> list[dict]:
    return []

3. OpenAPIConfig 側では Tag を enum から生成する

OpenAPIConfig 側では、enum から Tag オブジェクトを生成します。

from litestar import Litestar
from litestar.openapi import OpenAPIConfig
from litestar.openapi.spec import Tag, ExternalDocumentation

OPENAPI_TAGS = [
    Tag(
        name=ApiTag.USERS.value,
        description="ユーザー関連API",
    ),
    Tag(
        name=ApiTag.ITEMS.value,
        description="商品関連API",
    ),
    Tag(
        name=ApiTag.INTERNAL.value,
        description="内部向けAPI",
        external_docs=ExternalDocumentation(
            description="内部設計資料",
            url="https://example.com/internal-api-docs",
        ),
    ),
]

app = Litestar(
    route_handlers=[list_users],
    openapi_config=OpenAPIConfig(
        title="My API",
        version="1.0.0",
        tags=OPENAPI_TAGS,
    ),
)

こうしておくと、ルート側のタグ名と OpenAPI 上のタグ定義が必ず一致します。

さらにおすすめの形:enum + メソッド

Enum だけだと description を別管理しがちなので、少し踏み込むなら enum にメソッドを追加するのが扱いやすいです。

from enum import StrEnum
from litestar.openapi.spec import Tag

class ApiTag(StrEnum):
    USERS = "users"
    ITEMS = "items"
    INTERNAL = "internal"

    @property
    def description(self) -> str:
        return {
            ApiTag.USERS: "ユーザー関連API",
            ApiTag.ITEMS: "商品関連API",
            ApiTag.INTERNAL: "内部向けAPI",
        }[self]

    def as_openapi_tag(self) -> Tag:
        return Tag(name=self.value, description=self.description)

openapi_config = OpenAPIConfig(
    title="My API",
    version="1.0.0",
    tags=[tag.as_openapi_tag() for tag in ApiTag],
)

ルート側は変わりません。

@get("/users", tags=[ApiTag.USERS.value])
async def list_users() -> list[dict]:
    return []

この形だと、タグ追加時に定義漏れが起きにくくなります。

「両方に登録しないといけないのか?」について

厳密には、用途が異なるため「同じものを二重管理している」というより、以下のように考えるべきです。

  • ルート側: このAPIはどのタグに属するか
  • OpenAPIConfig.tags: そのタグは何者か(説明文など)

そのため、description など不要なら OpenAPIConfig.tags を省く運用もあります。ただし、Swagger / Scalar / ReDoc 上でタグ説明をきれいに出したいなら、OpenAPIConfig.tags も持っておく価値があります。

推奨される実装パターン

最終的には、以下のパターンが最も無難です。

  1. StrEnum でタグ名を定義
  2. ルート側は tags=[ApiTag.X.value]
  3. OpenAPIConfig.tags は enum から自動生成
  4. description / external docs も enum 側に寄せる

つまり、**「二重登録」ではなく「単一ソースから2箇所へ展開」**にする、という設計です。

最後に

Litestar でタグを管理する際、この方法を採用することで以下のメリットが得られます。

  • タグ名の typo を防ぐ
  • IDE の補完機能を活用できる
  • 定義漏れを防ぐ
  • 保守性が向上する

ただし、プロジェクトの規模や要件によって、最適な実装方法は異なるかもしれません。

皆さんは、実務で Litestar のタグをどのように管理していますか?より良い方法や工夫があれば、ぜひコメントで教えてください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?