Communitygithub.com

API 設計パターン

プロダクションAPI向けのリソース命名、ステータスコード、ページネーション、フィルタリング、エラーレスポンス、バージョニング、レート制限を含むREST API設計パターン。RESTエンドポイント、リソース名、ステータスコード、ページネーション、バージョニングを設計またはレビューする際に使用します。

API 設計パターン とは?

このスキルは、一貫性があり開発者にとって使いやすいREST APIを設計するためのベストプラクティスと規約を提供します。新しいAPIエンドポイントの設計、既存のAPI契約のレビュー、ページネーションやフィルタリングの実装、エラーハンドリング、バージョニング戦略の策定など、API設計に関連するあらゆるフェーズで役立ちます。リソース設計では、複数形、小文字、ケバブケースの命名規則に従い、サブリソースを使った関係の表現方法を推奨。HTTPメソッドとステータスコードのセマンティクスを明確に定義し、200番で全てを返すような誤った使い方を防ぎます。オフセットベースとカーソルベースのページネーションの使い分け、フィルタリング、ソート、検索の実装パターン、そしてトークンベースの認証・認可、レート制限、URLパスバージョニングの推奨事項を含みます。最後に、TypeScript (Next.js)、Python (Django REST Framework)、Go (net/http) の実装例と、新しいエンドポイントをリリースする前のチェックリストが提供されます。

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

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

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

ドキュメント

API設計パターン

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

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

  • 新しい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: 201とLocationヘッダーを使用
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;   -- has_nextを判定するために1つ多く取得
{
  "data": [...],
  "meta": {
    "has_next": true,
    "next_cursor": "eyJpZCI6MTQzfQ"
  }
}

長所: 位置に関係なくパフォーマンスが安定、同時挿入時に一貫性がある 短所: 任意のページにジャンプできない、カーソルは不透明

どちらを使うべきか

ユースケースページネーションタイプ
管理ダッシュボード、小規模データセット(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/minIP単位公開エンドポイント
認証済み100/minユーザー単位標準APIアクセス
プレミアム1000/minAPIキー単位有料APIプラン
内部10000/minサービス単位サービス間通信

バージョニング

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/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

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.

関連スキル