mcpField
A
api-design

api-design

github.com/travisjneuman開発107167 回インストール今日更新
リポジトリ

api-design について

api-designは、保守しやすいRESTなどのAPIを設計する開発者向けのリファレンススキルです。リソース命名からセキュリティ、プロトコル、配信パターンまで扱います。

主な機能

  1. 01RESTのリソースとメソッドの規約
  2. 02ステータスコードとレスポンス形式
  3. 03ページネーション、フィルタリング、ソート、バージョン管理
  4. 04OpenAPI、gRPC、tRPCのガイダンス
  5. 05認証、レート制限、セキュリティ
  6. 06Webhookの署名、再試行、冪等性

活用シーン

  1. 01新しいREST APIのエンドポイント設計
  2. 02ページネーションとバージョン管理方式の選択
  3. 03認証とセキュリティ要件の整理
  4. 04信頼性の高いWebhook配信の設計

インストール

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

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

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

詳細

API 設計ガイド

開発者にとって使いやすく、保守しやすい API を設計するためのベストプラクティスです。

REST API の原則

リソースの命名

動詞ではなく名詞を使います:

✓ GET /users
✓ GET /users/123
✓ GET /users/123/orders

✗ GET /getUsers
✗ GET /fetchUserById
✗ POST /createNewOrder

複数形の名詞を使います:

✓ /users
✓ /orders
✓ /products

✗ /user
✗ /order

階層関係:

/users/{userId}/orders           # User's orders
/users/{userId}/orders/{orderId} # Specific order

HTTP メソッド
メソッド用途冪等性リクエストボディ
GETリソースの取得はいいいえ
POSTリソースの作成いいえはい
PUTリソース全体の置換はいはい
PATCHリソースの部分更新いいえはい
DELETEリソースの削除はいいいえ

ステータスコード

成功(2xx):

  • 200 OK - リクエストが成功しました
  • 201 Created - リソースが作成されました(Location ヘッダーを返します)
  • 204 No Content - レスポンスボディなしで成功しました

クライアントエラー(4xx):

  • 400 Bad Request - リクエストの形式が正しくありません
  • 401 Unauthorized - 認証が必要です
  • 403 Forbidden - 権限がありません
  • 404 Not Found - リソースが存在しません
  • 409 Conflict - 状態が競合しています
  • 422 Unprocessable Entity - 検証に失敗しました
  • 429 Too Many Requests - レート制限を超過しました

サーバーエラー(5xx):

  • 500 Internal Server Error - 予期しないエラーです
  • 503 Service Unavailable - 一時的にサービスを利用できません

レスポンス形式

成功レスポンス:

{
  "data": {
    "id": "123",
    "name": "John Doe",
    "email": "[email protected]"
  }
}

コレクションレスポンス:

{
  "data": [
    { "id": "1", "name": "Item 1" },
    { "id": "2", "name": "Item 2" }
  ],
  "meta": {
    "page": 1,
    "perPage": 20,
    "total": 100,
    "totalPages": 5
  },
  "links": {
    "self": "/items?page=1",
    "next": "/items?page=2",
    "prev": null
  }
}

エラーレスポンス:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": [
      {
        "field": "email",
        "code": "INVALID_FORMAT",
        "message": "Must be a valid email address"
      }
    ]
  }
}

ページネーション

オフセット方式(シンプルですが、大規模なデータセットでは遅くなります):

GET /users?page=2&perPage=20
GET /users?offset=40&limit=20

カーソル方式(効率的で、推奨されます):

GET /users?cursor=eyJpZCI6MTIzfQ&limit=20

レスポンスには次のカーソルが含まれます:

{
  "data": [...],
  "meta": {
    "nextCursor": "eyJpZCI6MTQzfQ",
    "hasMore": true
  }
}

フィルタリング、並べ替え、フィールド

フィルタリング:

GET /users?status=active
GET /users?created_after=2024-01-01
GET /users?role=admin,moderator

並べ替え:

GET /users?sort=name
GET /users?sort=-created_at         # Descending
GET /users?sort=status,-created_at  # Multiple fields

フィールドの選択:

GET /users?fields=id,name,email
GET /users?include=orders,profile

バージョニング

URL パス(推奨):

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

ヘッダー:

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

OpenAPI 仕様

openapi: 3.0.3
info:
  title: My API
  version: 1.0.0
  description: API for managing users

servers:
  - url: https://api.example.com/v1

paths:
  /users:
    get:
      summary: List users
      tags: [Users]
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
      responses:
        "200":
          description: List of users
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/User"

components:
  schemas:
    User:
      type: object
      required: [id, email]
      properties:
        id:
          type: string
        email:
          type: string
          format: email
        name:
          type: string

認証

API キー(シンプルで、サーバー間通信向け):

Authorization: Api-Key YOUR_API_KEY

Bearer トークン(JWT、OAuth):

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

OpenAPI への記載:

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

security:
  - bearerAuth: []

レート制限

レスポンスにヘッダーを含めます:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200

制限を超過した場合は、429 Too Many Requests を返します。


セキュリティチェックリスト

  • HTTPS のみを使用
  • 保護されたルートで認証を実施
  • 入力値の検証
  • 出力のエンコード
  • レート制限
  • CORS の設定
  • URL に機密データを含めない
  • 監査ログの記録

gRPC と Protocol Buffers

Proto 定義
syntax = "proto3";
package user.v1;

service UserService {
  rpc GetUser(GetUserRequest) returns (User);
  rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
  rpc CreateUser(CreateUserRequest) returns (User);
  rpc StreamUpdates(StreamRequest) returns (stream UserUpdate);
}

message User {
  string id = 1;
  string name = 2;
  string email = 3;
  google.protobuf.Timestamp created_at = 4;
}

message GetUserRequest {
  string id = 1;
}

message ListUsersRequest {
  int32 page_size = 1;
  string page_token = 2;
}

message ListUsersResponse {
  repeated User users = 1;
  string next_page_token = 2;
}
gRPC と REST の使い分け
要素gRPCREST
パフォーマンスバイナリ、高速JSON、人間が読みやすい
ストリーミング双方向SSE/WebSocket による代替手段
型安全性Proto が型を生成OpenAPI + コード生成
ブラウザーgRPC-Web プロキシが必要ネイティブ
ツールProtoc、BufSwagger、Postman
最適な用途サービス間通信、ストリーミングパブリック API、Web クライアント

TypeScript 向け tRPC

// server/router.ts
import { router, publicProcedure, protectedProcedure } from './trpc';
import { z } from 'zod';

export const appRouter = router({
  user: router({
    get: publicProcedure
      .input(z.object({ id: z.string() }))
      .query(async ({ input }) => {
        return db.user.findUnique({ where: { id: input.id } });
      }),
    create: protectedProcedure
      .input(z.object({
        name: z.string().min(1),
        email: z.string().email(),
      }))
      .mutation(async ({ input, ctx }) => {
        return db.user.create({ data: { ...input, createdBy: ctx.userId } });
      }),
  }),
});

export type AppRouter = typeof appRouter;

// client.ts - Full type inference, no codegen
const user = trpc.user.get.useQuery({ id: '123' });
const createUser = trpc.user.create.useMutation();

tRPC は、クライアントとサーバーが同じコードベースを共有するモノレポのフルスタック TypeScript アプリに最適です。


Webhook 設計パターン

Webhook ペイロード
{
  "id": "evt_abc123",
  "type": "order.completed",
  "created_at": "2025-01-15T10:30:00Z",
  "data": {
    "order_id": "ord_456",
    "total": 99.99,
    "currency": "USD"
  }
}
署名の検証
// Sign webhooks with HMAC-SHA256
import crypto from 'crypto';

function signWebhook(payload: string, secret: string): string {
  return crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
}

// Verify on receiving end
function verifyWebhook(payload: string, signature: string, secret: string): boolean {
  const expected = signWebhook(payload, secret);
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected),
  );
}
再試行戦略
Attempt 1: Immediately
Attempt 2: After 1 minute
Attempt 3: After 5 minutes
Attempt 4: After 30 minutes
Attempt 5: After 2 hours
Attempt 6: After 24 hours (final)

Failed webhooks: log, alert, manual retry UI
冪等性
// Include idempotency key in webhook
// Receivers should deduplicate based on event ID
async function handleWebhook(event: WebhookEvent) {
  // Check if already processed
  const existing = await db.processedEvents.findUnique({
    where: { eventId: event.id },
  });
  if (existing) return { status: 'already_processed' };

  // Process and record
  await db.$transaction([
    processEvent(event),
    db.processedEvents.create({ data: { eventId: event.id } }),
  ]);
}

API バージョニング戦略

戦略例メリットデメリット
URL パス/api/v1/users明示的で、ルーティングが簡単ですURL が煩雑になります
クエリパラメーター/api/users?version=1オプションのパラメーターです見落としやすいです
ヘッダーAccept: application/vnd.api.v1URL がすっきりします見えにくく、テストが難しくなります
コンテントネゴシエーションAccept: application/json;v=2標準に準拠しています実装が複雑です

推奨: シンプルさを重視して URL パスによるバージョニングを採用します。後方互換性のない変更の場合にのみメジャーバージョンを上げます。同じバージョン内では、追加的で後方互換性のある変更を行います。

コメント