mcpField
A
api-design-principles

api-design-principles

github.com/secondsky開発240472 回インストール5 週間前に更新
リポジトリ

api-design-principles について

api-design-principlesは、RESTとGraphQLのAPIを設計・レビューするためのスキルです。リソースやスキーマの設計、バージョン管理、ページネーション、エラー処理に加え、保守性と開発者向けの実践を扱います。

主な機能

  1. 01RESTのリソースとHTTPメソッド設計
  2. 02GraphQLのスキーマとクエリパターン
  3. 03APIのバージョン管理戦略
  4. 04ページネーションとエラー処理
  5. 05DataLoaderとAPIドキュメントの実践

活用シーン

  1. 01新しいRESTまたはGraphQL APIの設計
  2. 02API仕様のレビュー
  3. 03エンドポイントやスキーマの改修
  4. 04チーム内のAPI規約の策定

インストール

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

$npx skills add https://github.com/secondsky/claude-skills --skill api-design-principles

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

詳細

API 設計の原則

REST と GraphQL の API 設計原則を習得し、直感的で、スケーラブルかつ保守しやすく、開発者に喜ばれ、長く使い続けられる API を構築しましょう。

このスキルを使う場面

  • 新しい REST または GraphQL API を設計するとき
  • 使いやすさを高めるために既存の API をリファクタリングするとき
  • チームの API 設計標準を確立するとき
  • 実装前に API 仕様をレビューするとき
  • API パラダイム間を移行するとき(REST から GraphQL など)
  • 開発者にとって使いやすい API ドキュメントを作成するとき
  • 特定のユースケース(モバイル、サードパーティとの連携など)に合わせて API を最適化するとき

コアコンセプト

1. RESTful 設計の原則

リソース指向アーキテクチャ

  • リソースは動詞ではなく名詞です(ユーザー、注文、製品など)
  • アクションには HTTP メソッドを使用します(GET、POST、PUT、PATCH、DELETE)
  • URL はリソースの階層を表します
  • 一貫した命名規則を使用します

HTTP メソッドのセマンティクス:

  • GET:リソースを取得します(冪等かつ安全)
  • POST:新しいリソースを作成します
  • PUT:リソース全体を置き換えます(冪等)
  • PATCH:リソースを部分的に更新します
  • DELETE:リソースを削除します(冪等)
2. GraphQL 設計の原則

スキーマファースト開発

  • 型でドメインモデルを定義します
  • クエリでデータを読み取ります
  • ミューテーションでデータを変更します
  • サブスクリプションでリアルタイム更新を行います

クエリ構造:

  • クライアントは必要なデータだけを正確にリクエストします
  • 単一のエンドポイントで複数の操作を実行できます
  • 厳密に型付けされたスキーマを使用します
  • イントロスペクションが組み込まれています
3. API のバージョニング戦略

URL バージョニング:

/api/v1/users
/api/v2/users

ヘッダーバージョニング:

Accept: application/vnd.api+json; version=1

クエリパラメータバージョニング:

/api/users?version=1

REST API 設計パターン

パターン 1:リソースコレクションの設計
# Good: Resource-oriented endpoints
GET    /api/users              # List users (with pagination)
POST   /api/users              # Create user
GET    /api/users/{id}         # Get specific user
PUT    /api/users/{id}         # Replace user
PATCH  /api/users/{id}         # Update user fields
DELETE /api/users/{id}         # Delete user

# Nested resources
GET    /api/users/{id}/orders  # Get user's orders
POST   /api/users/{id}/orders  # Create order for user

# Bad: Action-oriented endpoints (avoid)
POST   /api/createUser
POST   /api/getUserById
POST   /api/deleteUser
パターン 2:ページネーションとフィルタリング
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
from typing import List, Optional

class PaginatedResponse(BaseModel):
    items: List[dict]
    total: int
    page: int
    page_size: int
    pages: int

    @property
    def has_next(self) -> bool:
        return self.page < self.pages

app = FastAPI()

@app.get("/api/users", response_model=PaginatedResponse)
async def list_users(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    status: Optional[str] = Query(None),
    search: Optional[str] = Query(None)
):
    query = build_query(status=status, search=search)
    total = await count_users(query)
    offset = (page - 1) * page_size
    users = await fetch_users(query, limit=page_size, offset=offset)

    return PaginatedResponse(
        items=users,
        total=total,
        page=page,
        page_size=page_size,
        pages=(total + page_size - 1) // page_size
    )
パターン 3:エラー処理とステータスコード
from fastapi import HTTPException, status

STATUS_CODES = {
    "success": 200,
    "created": 201,
    "no_content": 204,
    "bad_request": 400,
    "unauthorized": 401,
    "forbidden": 403,
    "not_found": 404,
    "conflict": 409,
    "unprocessable": 422,
    "internal_error": 500
}

def raise_not_found(resource: str, id: str):
    raise HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail={
            "error": "NotFound",
            "message": f"{resource} not found",
            "details": {"id": id}
        }
    )
パターン 4:HATEOAS(アプリケーション状態のエンジンとしてのハイパーメディア)
class UserResponse(BaseModel):
    id: str
    name: str
    email: str
    _links: dict

    @classmethod
    def from_user(cls, user: User, base_url: str):
        return cls(
            id=user.id,
            name=user.name,
            email=user.email,
            _links={
                "self": {"href": f"{base_url}/api/users/{user.id}"},
                "orders": {"href": f"{base_url}/api/users/{user.id}/orders"},
                "update": {"href": f"{base_url}/api/users/{user.id}", "method": "PATCH"},
                "delete": {"href": f"{base_url}/api/users/{user.id}", "method": "DELETE"}
            }
        )

GraphQL 設計パターン

パターン 1:スキーマ設計
type User {
  id: ID!
  email: String!
  name: String!
  orders(first: Int = 20, after: String): OrderConnection!
}

type OrderConnection {
  edges: [OrderEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

type Mutation {
  createUser(input: CreateUserInput!): CreateUserPayload!
}

input CreateUserInput {
  email: String!
  name: String!
  password: String!
}

type CreateUserPayload {
  user: User
  errors: [Error!]
}
パターン 2:DataLoader(N+1 問題の防止)
from aiodataloader import DataLoader

class UserLoader(DataLoader):
    async def batch_load_fn(self, user_ids: List[str]) -> List[Optional[dict]]:
        users = await fetch_users_by_ids(user_ids)
        user_map = {user["id"]: user for user in users}
        return [user_map.get(user_id) for user_id in user_ids]

ベストプラクティス

REST API
  1. 一貫した命名:コレクションには複数形の名詞を使用します
  2. ステートレス:各リクエストに必要な情報をすべて含めます
  3. HTTP ステータスコードを正しく使う:2xx は成功、4xx はクライアントエラー、5xx はサーバーエラーです
  4. API にバージョンを付ける:初日から破壊的変更に備えて計画します
  5. ページネーション:大規模なコレクションには必ずページネーションを適用します
  6. レート制限:レート制限で API を保護します
  7. ドキュメント:インタラクティブなドキュメントには OpenAPI/Swagger を使用します
GraphQL API
  1. スキーマファースト:リゾルバーを書く前にスキーマを設計します
  2. N+1 問題の回避:効率よくデータを取得するために DataLoader を使用します
  3. 入力値の検証:スキーマとリゾルバーの両方のレベルで検証します
  4. エラー処理:ミューテーションのペイロードに構造化されたエラーを返します
  5. ページネーション:カーソルベースのページネーション(Relay 仕様)を使用します
  6. 非推奨化:段階的な移行には @deprecated ディレクティブを使用します
  7. モニタリング:クエリの複雑度と実行時間を追跡します

よくある落とし穴

  • 過剰取得/取得不足(REST):GraphQL では解消できますが、DataLoader が必要です
  • 破壊的変更:API にバージョンを付けるか、非推奨化の戦略を使用します
  • 一貫性のないエラー形式:エラーレスポンスを標準化します
  • レート制限の欠如:制限のない API は不正利用に対して脆弱です
  • 不十分なドキュメント:ドキュメントのない API は開発者を困らせます
  • HTTP セマンティクスの無視:冪等な操作に POST を使うと、期待に反します
  • 密結合:API の構造をデータベーススキーマに合わせるべきではありません

コメント