mcpField
A
api-design-patterns

api-design-patterns

github.com/hieutrtr開発870 回インストール39 週間前に更新
リポジトリ

api-design-patterns について

api-design-patterns は、実装前の API 契約の設計とレビューに使うスキルです。エンドポイントの命名、スキーマ、ページネーション、エラー、バージョニング、ドキュメントを案内し、設計内容を api-design.md にまとめます。

主な機能

  1. 01URL と HTTP メソッドの規約
  2. 02リクエストとレスポンスのスキーマ統一
  3. 03ページネーション、フィルタリング、ソート
  4. 04エラー形式とバージョニングの定義
  5. 05OpenAPI ドキュメント作成の指針

活用シーン

  1. 01新しい API 契約の設計
  2. 02エンドポイントの一貫性レビュー
  3. 03ページネーションとフィルタリングの計画
  4. 04API エラーとバージョン方針の定義

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/hieutrtr/ai1-skills --skill api-design-patterns

インストール後、Claude Code で/api-design-patternsを入力して読み込まれたか確認します。

同梱ファイルreferences/endpoint-catalog-template.mdreferences/pydantic-schema-examples.md

詳細

API 設計パターン

使用する場面

次の場合にこのスキルを使用します。

  • 新しい API エンドポイントを設計する場合、または既存のエンドポイントの契約を変更する場合
  • 機能のリクエスト/レスポンススキーマを定義する場合
  • エンドポイント間でページネーション、フィルタリング、ソートを標準化する場合
  • 一貫性のあるエラーレスポンス形式を設計する場合
  • API のバージョン管理や非推奨化の方針を計画する場合
  • 実装前に API 契約の一貫性をレビューする場合
  • フロントエンド/バックエンド間の連携のため、エンドポイントの仕様を文書化する場合

入力: plan.md または architecture.md が存在する場合は、機能の範囲とアーキテクチャ上の決定事項を把握するために読み込みます。それ以外の場合は、ユーザーのリクエストに基づいて作業します。

出力: API 設計を api-design.md に書き込みます。ユーザーには次のように伝えます。「API 設計を api-design.md に書き込みました。/task-decomposition を実行して実装タスクを作成するか、/python-backend-expert を実行して実装してください。」

次の場合、このスキルを使用しないでください。

  • 実装コードを書く場合(python-backend-expert を使用)
  • システムレベルのアーキテクチャを決定する場合(system-architecture を使用)
  • エンドポイントのテストを書く場合(pytest-patterns を使用)
  • フロントエンドのデータ取得を実装する場合(react-frontend-expert を使用)

手順

URL の命名規則
リソースの命名ルール
  1. コレクションには複数形の名詞を使用します:/users、/orders、/products
  2. 複数語のリソースにはケバブケースを使用します:/order-items、/user-profiles
  3. ID で指定するリソースには単数形を使用します:/users/{user_id}、/orders/{order_id}
  4. ネストは最大 2 階層にします:/users/{user_id}/orders(/users/{user_id}/orders/{order_id}/items/{item_id} のようにはしません)
  5. URL に動詞を含めません。代わりに HTTP メソッドを使用します(/orders への POST とし、/orders/create にはしません)
  6. フィルタリング、ソート、ページネーションにはクエリパラメーターを使用します:/users?role=admin&sort=-created_at
URL 構造のテンプレート
/{version}/{resource}                    → Collection (list, create)
/{version}/{resource}/{id}               → Single resource (get, update, delete)
/{version}/{resource}/{id}/{sub-resource} → Nested collection
/{version}/{resource}/actions/{action}   → Non-CRUD operations (rarely needed)
命名例
良い例悪い例理由
GET /v1/usersGET /v1/getUsers動詞は不要です。HTTP メソッドがアクションを示します
POST /v1/usersPOST /v1/user/createコレクションへの POST は作成を意味します
GET /v1/order-itemsGET /v1/orderItemsキャメルケースではなく、ケバブケースを使用します
GET /v1/users/{id}/ordersGET /v1/users/{id}/orders/{oid}/itemsネストは最大 2 階層です
POST /v1/orders/{id}/actions/cancelPOST /v1/cancelOrder/{id}CRUD 以外の操作にはアクション用サブリソースを使用します
HTTP メソッドの意味
メソッド目的リクエストボディ成功ステータス冪等性
GETリソースを取得するなし200 OKあり
POST新しいリソースを作成する必須201 Createdなし
PUT全体を置き換える必須(全体)200 OKあり
PATCH部分的に更新する必須(部分)200 OKなし*
DELETEリソースを削除するなし204 No Contentあり

*PATCH は本質的に冪等ではありませんが、適切に実装すれば冪等にできます。

作成時のレスポンスヘッダー:

  • 201 を返す POST には、作成されたリソースの URL を示す Location ヘッダーを含めることが望ましいです。

条件付きリクエスト:

  • 頻繁にアクセスされるリソースを持つ GET エンドポイントで、キャッシュのために If-None-Match/ETag をサポートします
スキーマの命名規則(Pydantic v2)

すべての Pydantic スキーマで、一貫した命名パターンに従ってください。

パターン用途フィールド
{Resource}CreatePOST リクエストボディ書き込み可能なフィールド。id とタイムスタンプは含めません
{Resource}UpdatePUT リクエストボディすべての書き込み可能なフィールドが必須です
{Resource}PatchPATCH リクエストボディすべてのフィールドが Optional です
{Resource}Response単一リソースのレスポンスid、タイムスタンプを含むすべてのフィールド
{Resource}ListResponseページネーションされたリストのレスポンスitems + ページネーションのメタデータ
{Resource}FilterクエリパラメーターOptional なフィルターフィールド

スキーマ設計ルール:

  • Response スキーマでは、内部フィールド(hashed_password、internal_notes)を決して公開しないでください
  • Response スキーマには必ず id とタイムスタンプ(created_at、updated_at)を含めてください
  • ORM モデルをレスポンススキーマに変換するには model_validate(orm_instance) を使用してください
  • PATCH 操作では model_dump(exclude_unset=True) を使用して、「未指定」と「null に設定」を区別してください
  • 具体例は references/pydantic-schema-examples.md を参照してください
ページネーション
カーソルベースのページネーション(デフォルト)

すべてのリストエンドポイントでカーソルベースのページネーションを使用してください。大規模なデータセットではオフセットベースよりパフォーマンスが高く、「ウィンドウのずれ」の問題も回避できます。

リクエストパラメーター:

GET /v1/users?cursor=eyJpZCI6MTAwfQ&limit=20
パラメーター型デフォルト説明
cursorstr | NoneNone前回のレスポンスから取得した不透明なカーソル
limitint201 ページあたりの項目数(最大 100)

レスポンス形式:

{
  "items": [...],
  "next_cursor": "eyJpZCI6MTIwfQ",
  "has_more": true
}

カーソルの実装:

  • 最後の項目のソートキー(通常は id)を Base64 文字列としてエンコードします
  • カーソルはクライアントにとって不透明なものであり、解析や生成をしてはいけません
  • WHERE id > :last_id ORDER BY id ASC LIMIT :limit + 1 を使用します。has_more を判定するため、1 件多く取得します
オフセットベースのページネーション(必要な場合)

クライアントが任意のページに移動する必要がある場合(管理者用テーブルなど)に限り、オフセットベースを使用してください。

{
  "items": [...],
  "total": 150,
  "page": 2,
  "page_size": 20,
  "total_pages": 8
}
フィルタリングと並べ替え
フィルタリング

フィールド名を使ったクエリパラメーターを使用してください。

GET /v1/users?role=admin&is_active=true&created_after=2024-01-01

フィルタリングの規則:

  • 完全一致:?field=value
  • 範囲:?field_min=10&field_max=100 または ?created_after=...&created_before=...
  • 検索:?q=search+term(複数のフィールドを対象とする全文検索)
  • 複数の値:?status=active&status=pending(OR の意味)
並べ替え

フィールド名と方向のプレフィックスを指定する sort クエリパラメーターを使用してください。

GET /v1/users?sort=-created_at        → descending by created_at
GET /v1/users?sort=name               → ascending by name
GET /v1/users?sort=-created_at,name   → multi-field sort

規則: - プレフィックスは降順、プレフィックスなしは昇順を意味します。

エラーレスポンス形式

すべての API エラーは、一貫した形式に従います。

{
  "detail": "Human-readable error message",
  "code": "MACHINE_READABLE_CODE",
  "field_errors": [
    {
      "field": "email",
      "message": "Invalid email format",
      "code": "INVALID_FORMAT"
    }
  ]
}
標準エラーコードとステータスの対応
HTTP ステータス使用する場合例 code
400不正なリクエストBAD_REQUEST
401認証情報がない、または無効UNAUTHORIZED
403認証済みだが、認可されていないFORBIDDEN
404リソースが見つからないNOT_FOUND
409競合(重複、バージョンの不一致)CONFLICT
422検証エラー(Pydantic)VALIDATION_ERROR
429レート制限を超過RATE_LIMITED
500予期しないサーバーエラーINTERNAL_ERROR

エラースキーマ(Pydantic v2):

class FieldError(BaseModel):
    field: str
    message: str
    code: str

class ErrorResponse(BaseModel):
    detail: str
    code: str
    field_errors: list[FieldError] = []
API のバージョン管理
戦略:URL プレフィックスによるバージョン管理
/v1/users    → Version 1
/v2/users    → Version 2

バージョン管理のルール:

  1. 新しい API はすべて /v1/ から開始します
  2. 破壊的変更の場合のみメジャーバージョンを上げます
  3. 破壊的でない変更(新しい任意フィールド、新しいエンドポイント)では、新しいバージョンは不要です
  4. 同時にサポートするアクティブなバージョンは最大 2 つです

新しいバージョンが必要となる破壊的変更:

  • レスポンスからフィールドを削除する
  • フィールドの型を変更する
  • リクエストの任意フィールドを必須にする
  • 既存のエンドポイントの URL 構造を変更する
  • エラーレスポンスの形式を変更する

非推奨化の手順:

  1. 古いバージョンに Deprecation ヘッダーを追加します:Deprecation: true
  2. 廃止日を指定した Sunset ヘッダーを追加します:Sunset: Sat, 01 Mar 2026 00:00:00 GMT
  3. 新しいバージョンを指す Link ヘッダーを追加します:Link: </v2/users>; rel="successor-version"
  4. 監視のため、非推奨のエンドポイントの使用状況を記録します
  5. サンセット日以降に古いバージョンを削除します
OpenAPI ドキュメント

FastAPI は OpenAPI スキーマを自動的に生成します。以下の内容を追加します:

@router.get(
    "/users/{user_id}",
    response_model=UserResponse,
    summary="Get user by ID",
    description="Retrieve a single user's details by their unique identifier.",
    responses={
        404: {"model": ErrorResponse, "description": "User not found"},
    },
    tags=["Users"],
)
async def get_user(user_id: int) -> UserResponse:
    ...

ドキュメント作成の規約:

  • すべてのエンドポイントに summary(短い説明)と任意の description(詳細な説明)を設定します
  • 200 以外のすべてのレスポンスを、そのスキーマとともにドキュメント化します
  • リソース名に合わせた tags でエンドポイントをグループ化します
  • response_model を使用してレスポンススキーマを自動的にドキュメント化します

例

Products API コントラクトの設計

目的: 検索とページネーションに対応する /v1/products CRUD エンドポイントのコントラクトを設計します。

エンドポイント:

MethodPathDescriptionRequestResponseStatus
GET/v1/products商品一覧クエリ:cursor、limit、q、category、sortProductListResponse200
POST/v1/products商品を作成ボディ:ProductCreateProductResponse201
GET/v1/products/{id}商品を取得—ProductResponse200
PATCH/v1/products/{id}商品を更新ボディ:ProductPatchProductResponse200
DELETE/v1/products/{id}商品を削除——204

スキーマ:

class ProductCreate(BaseModel):
    name: str = Field(min_length=1, max_length=200)
    description: str | None = None
    price_cents: int = Field(gt=0)
    category: str
    sku: str = Field(pattern=r"^[A-Z0-9-]+$")

class ProductPatch(BaseModel):
    name: str | None = None
    description: str | None = None
    price_cents: int | None = Field(default=None, gt=0)
    category: str | None = None

class ProductResponse(BaseModel):
    id: int
    name: str
    description: str | None
    price_cents: int
    category: str
    sku: str
    created_at: datetime
    updated_at: datetime

class ProductListResponse(BaseModel):
    items: list[ProductResponse]
    next_cursor: str | None
    has_more: bool

検索とフィルタリング:

GET /v1/products?q=laptop&category=electronics&sort=-price_cents&limit=20

完全なドキュメントテンプレートについては、references/endpoint-catalog-template.md を参照してください。 その他のスキーマ例については、references/pydantic-schema-examples.md を参照してください。

エッジケース

一括操作

複数のリソースを一度に操作する場合:

POST /v1/users/bulk

リクエスト:

{
  "items": [
    {"email": "[email protected]", "name": "Alice"},
    {"email": "[email protected]", "name": "Bob"}
  ]
}

レスポンス(一部成功 — ステータス 207):

{
  "results": [
    {"index": 0, "status": "created", "data": {...}},
    {"index": 1, "status": "error", "error": {"detail": "Email already exists", "code": "CONFLICT"}}
  ],
  "succeeded": 1,
  "failed": 1
}

個々の項目がそれぞれ独立して成功または失敗する場合は、HTTP 207 Multi-Status を使用します。

ファイルアップロードのエンドポイント

ファイルのアップロードには JSON ではなく、multipart/form-data を使用します:

@router.post("/v1/files", response_model=FileResponse, status_code=201)
async def upload_file(
    file: UploadFile,
    description: str = Form(default=""),
) -> FileResponse:
    ...

処理する前に、ファイルサイズと MIME タイプを検証します。サイズが大きすぎるファイルには 413 Payload Too Large を返します。

長時間実行される操作

通常のリクエストタイムアウト内に完了できない操作の場合:

  1. ステータス URL とともに 202 Accepted を返します:

    {"status_url": "/v1/jobs/abc123", "estimated_completion": "2024-01-15T10:30:00Z"}
    
  2. クライアントがステータス URL をポーリングします:

    GET /v1/jobs/abc123 → {"status": "processing", "progress": 0.65}
    GET /v1/jobs/abc123 → {"status": "completed", "result_url": "/v1/reports/xyz"}
    
サブリソースの設計

リソースが論理的に親リソースに属していても、ネストが 2 階層を超える場合は、フィルターを使ったトップレベルのリソースを使用します:

# Instead of: GET /v1/users/{id}/orders/{oid}/items
# Use:        GET /v1/order-items?order_id=123

これにより、フィルタリングを通じて関係を維持しながら、URL をフラットに保てます。

出力ファイル

プロジェクトルートの api-design.md に API 設計を書き込みます:

# API Design: [Feature Name]

## Endpoints

| Method | URL | Description | Auth |
|--------|-----|-------------|------|
| GET | /v1/users | List users | Required |
| POST | /v1/users | Create user | Required |

## Request/Response Schemas

### UserCreate
| Field | Type | Required | Validation |
|-------|------|----------|------------|
| email | string | Yes | Valid email |
| name | string | Yes | 1-100 chars |

### UserResponse
| Field | Type | Description |
|-------|------|-------------|
| id | uuid | User ID |
| email | string | User email |

## Error Codes

| Code | HTTP Status | Description |
|------|-------------|-------------|
| USER_NOT_FOUND | 404 | User does not exist |
| EMAIL_EXISTS | 409 | Email already registered |

## Next Steps
- Run `/task-decomposition` to create implementation tasks
- Run `/python-backend-expert` to implement endpoints

コメント