Communitygithub.com

API設計パターン

REST APIの設計パターン。リソースの命名、ステータスコード、ページネーション、フィルタリング、エラーレスポンス、バージョニング、レート制限など、実運用APIのためのベストプラクティスを提供します。RESTエンドポイントやリソース名、ステータスコード、ページネーション、バージョニングを設計・レビューする際に使用します。

API設計パターン とは?

このスキルは、プロダクション品質のREST APIを設計するための包括的なガイドラインを提供します。リソースの命名規則(複数形、ケバブケース、動詞禁止)、HTTPメソッドとステータスコードの正しい使い分け、一貫性のある成功・エラーレスポンス形式について詳しく説明します。特に、カーソルベースとオフセットベースのページネーション、フィルタリングとソート、レート制限ヘッダー、URLパスベースのバージョニング戦略など、実開発で直面する課題に対応したパターンが含まれています。TypeScript(Next.js)、Python(Django REST Framework)、Go(net/http)の各言語での実装例も提供し、抽象的な概念をコードに落とし込みやすくしています。エンドポイント出荷前のチェックリストもあり、設計レビューやコードレビューに直接活用できます。

対応~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/api-design

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

API設計パターン は何をしますか?

一貫性があり、開発者にとって使いやすいREST APIを設計するための規約とベストプラクティス。

アクティブにするタイミング

  • 新しいAPIエンドポイントの設計時
  • 既存のAPI契約のレビュー時
  • ページネーション、フィルタリング、ソートの追加時
  • APIのエラーハンドリング実装時
  • APIのバージョニング戦略の計画時
  • 公開またはパートナー向けAPIの構築時

リソース設計

URL構造

# リソースは名詞、複数形、小文字、ケバブケースで
GET    /api/v1/users
GET    /api/v1/users/:id
POST   /api/v1/users
PUT    /api/v1/users/:id
PATCH  /api/v1/users/:id
DELETE /api/v1/users/:id

# 関連を示すサブリソース
GET    /api/v1/users/:id/orders
POST   /api/v1/users/:id/orders

# CRUDにマッピングできないアクション(動詞は控えめに)
POST   /api/v1/orders/:id/cancel
POST   /api/v1/auth/login
POST   /api/v1/auth/refresh

命名規則

# GOOD
/api/v1/team-members          # 複数単語のリソースはケバブケース
/api/v1/orders?status=active  # フィルタリングはクエリパラメータ
/api/v1/users/123/orders      # 所有関係を示すネストされたリソース

# BAD
/api/v1/getUsers              # URLに動詞
/api/v1/user                  # 単数形(複数形を使用)
/api/v1/team_members          # URLにスネークケース
/api/v1/users/123/getOrders   # ネストされたリソースに動詞

HTTPメソッドとステータスコード

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

メソッドべき等安全使用目的
GETはいはいリソースの取得
POSTいいえいいえリソースの作成、アクションのトリガー
PUTはいいいえリソースの完全な置き換え
PATCHいいえ*いいえリソースの部分更新
DELETEはいいいえリソースの削除

*PATCHは適切に実装すればべき等にできる

ステータスコードリファレンス

# 成功
200 OK                    — GET, PUT, PATCH(レスポンスボディあり)
201 Created               — POST(Locationヘッダーを含める)
204 No Content            — DELETE, PUT(レスポンスボディなし)

# クライアントエラー
400 Bad Request           — バリデーション失敗、不正なJSON
401 Unauthorized          — 認証情報の欠落または無効
403 Forbidden             — 認証済みだが権限がない
404 Not Found             — リソースが存在しない
409 Conflict              — 重複エントリ、状態の競合
422 Unprocessable Entity  — 意味的に無効(JSONは正しいがデータが不正)
429 Too Many Requests     — レート制限超過

# サーバーエラー
500 Internal Server Error — 予期しない障害(詳細は絶対に公開しない)
502 Bad Gateway           — アップストリームサービスが失敗
503 Service Unavailable   — 一時的な過負荷、Retry-Afterヘッダーを含める

よくある間違い

# BAD: すべてに200を使う
{ "status": 200, "success": false, "error": "Not found" }

# GOOD: HTTPステータスコードを意味的に使用
HTTP/1.1 404 Not Found
{ "error": { "code": "not_found", "message": "User not found" } }

# BAD: バリデーションエラーに500を使う
# GOOD: フィールドレベルの詳細とともに400または422を使う

# BAD: 作成されたリソースに200を使う
# GOOD: Locationヘッダーとともに201を使う
HTTP/1.1 201 Created
Location: /api/v1/users/abc-123

レスポンス形式

成功レスポンス

{
  "data": {
    "id": "abc-123",
    "email": "[email protected]",
    "name": "Alice",
    "created_at": "2025-01-15T10:30:00Z"
  }
}

コレクションレスポンス(ページネーション付き)

{
  "data": [
    { "id": "abc-123", "name": "Alice" },
    { "id": "def-456", "name": "Bob" }
  ],
  "meta": {
    "total": 142,
    "page": 1,
    "per_page": 20,
    "total_pages": 8
  },
  "links": {
    "self": "/api/v1/users?page=1&per_page=20",
    "next": "/api/v1/users?page=2&per_page=20",
    "last": "/api/v1/users?page=8&per_page=20"
  }
}

エラーレスポンス

{
  "error": {
    "code": "validation_error",
    "message": "Request validation failed",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address",
        "code": "invalid_format"
      },
      {
        "field": "age",
        "message": "Must be between 0 and 150",
        "code": "out_of_range"
      }
    ]
  }
}

レスポンスラッパーのバリエーション

// オプションA: dataラッパー付き(公開APIに推奨)
interface ApiResponse<T> {
  data: T;
  meta?: PaginationMeta;
  links?: PaginationLinks;
}

interface ApiError {
  error: {
    code: string;
    message: string;
    details?: FieldError[];
  };
}

// オプションB: フラットなレスポンス(シンプル、内部APIで一般的)
// 成功: リソースを直接返す
// エラー: エラーオブジェクトを返す
// HTTPステータスコードで区別

ページネーション

オフセットベース(シンプル)

GET /api/v1/users?page=2&per_page=20

# 実装
SELECT * FROM users
ORDER BY created_at DESC
LIMIT 20 OFFSET 20;

長所: 実装が簡単、「Nページにジャンプ」をサポート 短所: 大きなオフセットで遅くなる(OFFSET 100000)、並行挿入時に不整合が生じる

カーソルベース(スケーラブル)

GET /api/v1/users?cursor=eyJpZCI6MTIzfQ&limit=20

# 実装
SELECT * FROM users
WHERE id > :cursor_id
ORDER BY id ASC
LIMIT 21;  -- 1つ多く取得してhas_nextを判定
{
  "data": [...],
  "meta": {
    "has_next": true,
    "next_cursor": "eyJpZCI6MTQzfQ"
  }
}

長所: 位置に関係なくパフォーマンスが安定、並行挿入時も一貫性を維持 短所: 任意のページにジャンプできない、カーソルは不透明(opaque)

どちらを使うべきか

ユースケースページネーションの種類
管理ダッシュボード、小規模データセット(<1万件)オフセット
無限スクロール、フィード、大規模データセットカーソル
公開APIカーソル(デフォルト)+オフセット(オプション)
検索結果オフセット(ユーザーはページ番号を期待)

フィルタリング、ソート、検索

フィルタリング

# 単純な一致
GET /api/v1/orders?status=active&customer_id=abc-123

# 比較演算子(ブラケット記法を使用)
GET /api/v1/products?price[gte]=10&price[lte]=100
GET /api/v1/orders?created_at[after]=2025-01-01

# 複数の値(カンマ区切り)
GET /api/v1/products?category=electronics,clothing

# ネストされたフィールド(ドット記法)
GET /api/v1/orders?customer.country=US

ソート

# 単一フィールド(降順はプレフィックス-)
GET /api/v1/products?sort=-created_at

# 複数フィールド(カンマ区切り)
GET /api/v1/products?sort=-featured,price,-created_at

全文検索

# 検索クエリパラメータ
GET /api/v1/products?q=wireless+headphones

# フィールド指定検索
GET /api/v1/users?email=alice

スパースフィールドセット

# 指定されたフィールドのみ返す(ペイロード削減)
GET /api/v1/users?fields=id,name,email
GET /api/v1/orders?fields=id,total,status&include=customer.name

認証と認可

トークンベースの認証

# AuthorizationヘッダーにBearerトークン
GET /api/v1/users
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

# APIキー(サーバー間通信用)
GET /api/v1/data
X-API-Key: sk_live_abc123

認可パターン

// リソースレベル: 所有権の確認
app.get("/api/v1/orders/:id", async (req, res) => {
  const order = await Order.findById(req.params.id);
  if (!order) return res.status(404).json({ error: { code: "not_found" } });
  if (order.userId !== req.user.id) return res.status(403).json({ error: { code: "forbidden" } });
  return res.json({ data: order });
});

// ロールベース: 権限の確認
app.delete("/api/v1/users/:id", requireRole("admin"), async (req, res) => {
  await User.delete(req.params.id);
  return res.status(204).send();
});

レート制限

ヘッダー

HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640000000

# 制限超過時
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Try again in 60 seconds."
  }
}

レート制限の階層

階層制限ウィンドウユースケース
匿名30/分IP単位公開エンドポイント
認証済み100/分ユーザー単位標準APIアクセス
プレミアム1000/分APIキー単位有料APIプラン
内部10000/分サービス単位サービス間通信

バージョニング

URLパスバージョニング(推奨)

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

長所: 明示的、ルーティングが容易、キャッシュ可能 短所: バージョン間でURLが変わる

ヘッダーバージョニング

GET /api/users
Accept: application/vnd.myapp.v2+json

長所: URLがクリーン 短所: テストが難しい、忘れやすい

バージョニング戦略

1. /api/v1/ から始める — 必要なときまでバージョニングしない
2. アクティブなバージョンは最大2つまで(現在+以前)
3. 非推奨のタイムライン:
   - 非推奨をアナウンス(公開APIの場合は6ヶ月前)
   - Sunsetヘッダーを追加: Sunset: Sat, 01 Jan 2026 00:00:00 GMT
   - サンセット日以降は410 Goneを返す
4. 互換性を壊さない変更は新しいバージョンを必要としない:
   - レスポンスに新しいフィールドを追加
   - 新しいオプションのクエリパラメータを追加
   - 新しいエンドポイントを追加
5. 互換性を壊す変更は新しいバージョンが必要:
   - フィールドの削除または名前変更
   - フィールドの型変更
   - URL構造の変更
   - 認証方法の変更

実装パターン

TypeScript(Next.js APIルート)

import { z } from "zod";
import { NextRequest, NextResponse } from "next/server";

const createUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1).max(100),
});

export async function POST(req: NextRequest) {
  const body = await req.json();
  const parsed = createUserSchema.safeParse(body);

  if (!parsed.success) {
    return NextResponse.json({
      error: {
        code: "validation_error",
        message: "Request validation failed",
        details: parsed.error.issues.map(i => ({
          field: i.path.join("."),
          message: i.message,
          code: i.code,
        })),
      },
    }, { status: 422 });
  }

  const user = await createUser(parsed.data);

  return NextResponse.json(
    { data: user },
    {
      status: 201,
      headers: { Location: `/api/v1/users/${user.id}` },
    },
  );
}

Python(Django REST Framework)

from rest_framework import serializers, viewsets, status
from rest_framework.response import Response

class CreateUserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    name = serializers.CharField(max_length=100)

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ["id", "email", "name", "created_at"]

class UserViewSet(viewsets.ModelViewSet):
    serializer_class = UserSerializer
    permission_classes = [IsAuthenticated]

    def get_serializer_class(self):
        if self.action == "create":
            return CreateUserSerializer
        return UserSerializer

    def create(self, request):
        serializer = CreateUserSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = UserService.create(**serializer.validated_data)
        return Response(
            {"data": UserSerializer(user).data},
            status=status.HTTP_201_CREATED,
            headers={"Location": f"/api/v1/users/{user.id}"},
        )

Go(net/http)

func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
    var req CreateUserRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        writeError(w, http.StatusBadRequest, "invalid_json", "Invalid request body")
        return
    }

    if err := req.Validate(); err != nil {
        writeError(w, http.StatusUnprocessableEntity, "validation_error", err.Error())
        return
    }

    user, err := h.service.Create(r.Context(), req)
    if err != nil {
        switch {
        case errors.Is(err, domain.ErrEmailTaken):
            writeError(w, http.StatusConflict, "email_taken", "Email already registered")
        default:
            writeError(w, http.StatusInternalServerError, "internal_error", "Internal error")
        }
        return
    }

    w.Header().Set("Location", fmt.Sprintf("/api/v1/users/%s", user.ID))
    writeJSON(w, http.StatusCreated, map[string]any{"data": user})
}

API設計チェックリスト

新しいエンドポイントをリリースする前に:

  • リソースURLが命名規則に従っているか(複数形、ケバブケース、動詞なし)
  • 正しいHTTPメソッドが使用されているか(読み取りはGET、作成はPOSTなど)
  • 適切なステータスコードが返されているか(すべてに200を使っていないか)
  • 入力がスキーマでバリデーションされているか(Zod, Pydantic, Bean Validation)
  • エラーレスポンスが標準形式(コードとメッセージ)に従っているか
  • リストエンドポイントにページネーションが実装されているか(カーソルまたはオフセット)
  • 認証が必要か(または明示的に公開としてマークされているか)
  • 認可がチェックされているか(ユーザーは自分のリソースのみアクセス可能か)
  • レート制限が設定されているか
  • レスポンスが内部の詳細(スタックトレース、SQLエラー)を漏洩していないか
  • 既存のエンドポイントと命名規則が一貫しているか(camelCase vs snake_case)
  • ドキュメント化されているか(OpenAPI/Swaggerスペックが更新されているか)

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

関連スキル