mcpField
R
rest-api-expert

rest-api-expert

github.com/cin12211開発22535 回インストール2 日前に更新
リポジトリ

rest-api-expert について

REST APIを設計、レビュー、改善する開発者向けのスキルです。HTTPのセマンティクス、エンドポイント設計、エラー処理、ページネーション、バージョン管理の指針をまとめています。

主な機能

  1. 01RESTエンドポイントとリソース設計
  2. 02HTTPメソッドとステータスコード
  3. 03一貫したエラーレスポンス
  4. 04ページネーションとAPIバージョン管理
  5. 05APIレビュー用チェックリスト

活用シーン

  1. 01動詞を含むAPIルートの再設計
  2. 02HTTPメソッドとステータスコードの確認
  3. 03エラー処理とページネーションの統一
  4. 04後方互換性を保つAPI変更の計画

インストール

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

$npx skills add https://github.com/cin12211/orca-q --skill rest-api-expert

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

詳細

REST API エキスパート

HTTP のセマンティクス、リソースモデリング、バージョニング戦略、エラー処理、API ドキュメントに関する深い知識を持つ、REST API 設計・開発のエキスパートです。

呼び出されたとき

ステップ 0:専門家を推奨して終了

問題が特に以下に関するものである場合:

  • GraphQL API:ここで終了し、GraphQL のパターンを検討してください
  • gRPC/Protocol Buffers:ここで終了し、適切なエキスパートを推奨してください
  • 認証の実装:ここで終了し、auth-expert を推奨してください
  • データベースクエリの最適化:ここで終了し、database-expert を推奨してください
環境の検出
# Check for API framework
grep -r "express\|fastify\|koa\|nestjs\|hono" package.json 2>/dev/null

# Check for OpenAPI/Swagger
ls -la swagger.* openapi.* 2>/dev/null
find . -name "*.yaml" -o -name "*.json" | xargs grep -l "openapi" 2>/dev/null | head -3

# Check existing API routes
find . -type f \( -name "*.ts" -o -name "*.js" \) -path "*/routes/*" -o -path "*/controllers/*" | head -10
戦略の適用
  1. API の設計上の問題または要件を特定します
  2. RESTful の原則とベストプラクティスを適用します
  3. 後方互換性とバージョニングを考慮します
  4. 適切なテストで検証します

問題別プレイブック

エンドポイントの設計

よくある問題:

  • RESTful ではない URL パターン(URL に動詞を使用)
  • 命名規則に一貫性がない
  • リソース階層が適切でない
  • リソース間の関係が欠落、または不明瞭

優先度順の修正:

  1. 最小限:エンドポイントを名詞に変更し、動詞を使わないようにします
  2. より良い方法:適切なリソース階層に再構成します
  3. 完全な対応:リンクを含む完全な HATEOAS を実装します

RESTful な URL 設計:

// ❌ BAD: Verb-based endpoints
GET    /getUsers
POST   /createUser
PUT    /updateUser/123
DELETE /deleteUser/123
GET    /getUserOrders/123

// ✅ GOOD: Resource-based endpoints
GET    /users              # List users
POST   /users              # Create user
GET    /users/123          # Get user
PUT    /users/123          # Update user (full)
PATCH  /users/123          # Update user (partial)
DELETE /users/123          # Delete user
GET    /users/123/orders   # User's orders (nested resource)

// ✅ GOOD: Filtering, sorting, pagination
GET    /users?status=active&sort=-createdAt&page=2&limit=20

// ✅ GOOD: Search as sub-resource
GET    /users/search?q=john&fields=name,email

// ✅ GOOD: Actions as sub-resources (when needed)
POST   /users/123/activate    # Action on resource
POST   /orders/456/cancel     # State transition

リソース:

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

よくある問題:

  • 状態を変更する操作に GET を使用している
  • ステータスコードの使用に一貫性がない
  • 適切なエラーコードが欠落している
  • 冪等性を無視している

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

// Method characteristics
// GET     - Safe, Idempotent, Cacheable
// POST    - Not Safe, Not Idempotent
// PUT     - Not Safe, Idempotent
// PATCH   - Not Safe, Not Idempotent
// DELETE  - Not Safe, Idempotent

// Express example with proper methods
import { Router } from 'express';

const router = Router();

// GET - Retrieve resources (safe, idempotent)
router.get('/products', listProducts);
router.get('/products/:id', getProduct);

// POST - Create resources (not idempotent)
router.post('/products', createProduct);

// PUT - Replace entire resource (idempotent)
router.put('/products/:id', replaceProduct);

// PATCH - Partial update (not idempotent typically)
router.patch('/products/:id', updateProduct);

// DELETE - Remove resource (idempotent)
router.delete('/products/:id', deleteProduct);

ステータスコードガイド:

// 2xx Success
200 OK              // GET success, PUT/PATCH success with body
201 Created         // POST success (include Location header)
204 No Content      // DELETE success, PUT/PATCH success without body

// 3xx Redirection
301 Moved Permanently  // Resource URL changed permanently
304 Not Modified       // Cached response is still valid

// 4xx Client Errors
400 Bad Request     // Invalid request body/params
401 Unauthorized    // Missing or invalid authentication
403 Forbidden       // Authenticated but not authorized
404 Not Found       // Resource doesn't exist
405 Method Not Allowed  // HTTP method not supported
409 Conflict        // State conflict (e.g., duplicate)
422 Unprocessable Entity  // Validation errors
429 Too Many Requests    // Rate limit exceeded

// 5xx Server Errors
500 Internal Server Error  // Unexpected server error
502 Bad Gateway           // Upstream service error
503 Service Unavailable   // Temporary overload/maintenance
エラー処理

よくある問題:

  • エラーレスポンス形式に一貫性がない
  • 内部エラーの詳細を公開している
  • クライアントでの処理に必要なエラーコードが欠落している
  • エラーに関するドキュメントがない

標準的なエラーレスポンス形式:

// Error response structure
interface ApiError {
  status: number;          // HTTP status code
  code: string;            // Application-specific error code
  message: string;         // Human-readable message
  details?: ErrorDetail[]; // Field-level errors (for validation)
  requestId?: string;      // For debugging/support
  timestamp?: string;      // ISO 8601
}

interface ErrorDetail {
  field: string;
  message: string;
  code: string;
}

// Example responses
// 400 Bad Request - Validation Error
{
  "status": 400,
  "code": "VALIDATION_ERROR",
  "message": "Request validation failed",
  "details": [
    { "field": "email", "message": "Invalid email format", "code": "INVALID_EMAIL" },
    { "field": "age", "message": "Must be at least 18", "code": "MIN_VALUE" }
  ],
  "requestId": "req_abc123",
  "timestamp": "2024-01-15T10:30:00Z"
}

// 404 Not Found
{
  "status": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "User with ID '123' not found",
  "requestId": "req_def456",
  "timestamp": "2024-01-15T10:30:00Z"
}

// 500 Internal Server Error
{
  "status": 500,
  "code": "INTERNAL_ERROR",
  "message": "An unexpected error occurred. Please try again later.",
  "requestId": "req_ghi789",
  "timestamp": "2024-01-15T10:30:00Z"
}

エラー処理ミドルウェア:

// Express error handler
import { Request, Response, NextFunction } from 'express';

class AppError extends Error {
  constructor(
    public status: number,
    public code: string,
    message: string,
    public details?: ErrorDetail[]
  ) {
    super(message);
  }
}

function errorHandler(
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
) {
  const requestId = req.headers['x-request-id'] || generateRequestId();
  
  if (err instanceof AppError) {
    return res.status(err.status).json({
      status: err.status,
      code: err.code,
      message: err.message,
      details: err.details,
      requestId,
      timestamp: new Date().toISOString(),
    });
  }
  
  // Log unexpected errors
  console.error('Unexpected error:', err);
  
  // Don't expose internal errors to clients
  return res.status(500).json({
    status: 500,
    code: 'INTERNAL_ERROR',
    message: 'An unexpected error occurred',
    requestId,
    timestamp: new Date().toISOString(),
  });
}
ページネーション

よくある問題:

  • ページネーションのパラメーターに一貫性がない
  • UI に必要な合計件数が欠落している
  • 大規模なデータセット向けのカーソルベースのオプションがない
  • オフセットベースのページネーションによるパフォーマンスの問題

ページネーション戦略:

// 1. Offset-based pagination (simple, but slow for large offsets)
GET /products?page=2&limit=20

{
  "data": [...],
  "pagination": {
    "page": 2,
    "limit": 20,
    "total": 150,
    "totalPages": 8,
    "hasNext": true,
    "hasPrev": true
  }
}

// 2. Cursor-based pagination (efficient for large datasets)
GET /products?cursor=eyJpZCI6MTAwfQ&limit=20

{
  "data": [...],
  "pagination": {
    "limit": 20,
    "nextCursor": "eyJpZCI6MTIwfQ",
    "prevCursor": "eyJpZCI6ODB9",
    "hasNext": true,
    "hasPrev": true
  }
}

// Implementation example
async function paginateWithCursor(
  cursor: string | null,
  limit: number = 20
) {
  const decodedCursor = cursor 
    ? JSON.parse(Buffer.from(cursor, 'base64').toString())
    : null;
    
  const items = await prisma.product.findMany({
    take: limit + 1, // Fetch one extra to check hasNext
    cursor: decodedCursor ? { id: decodedCursor.id } : undefined,
    skip: decodedCursor ? 1 : 0,
    orderBy: { id: 'asc' },
  });
  
  const hasNext = items.length > limit;
  const data = hasNext ? items.slice(0, -1) : items;
  
  return {
    data,
    pagination: {
      limit,
      nextCursor: hasNext 
        ? Buffer.from(JSON.stringify({ id: data[data.length - 1].id })).toString('base64')
        : null,
      hasNext,
    },
  };
}
API のバージョニング

よくある問題:

  • バージョニング戦略がない
  • バージョンを更新せずに互換性を破る変更を行っている
  • エンドポイント間でバージョニングに一貫性がない
  • 非推奨の通知がない

バージョニング戦略:

// 1. URL Path Versioning (recommended)
GET /api/v1/users
GET /api/v2/users

// Implementation
import { Router } from 'express';

const v1Router = Router();
const v2Router = Router();

// V1 routes
v1Router.get('/users', getUsersV1);

// V2 routes with breaking changes
v2Router.get('/users', getUsersV2);

app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);

// 2. Header Versioning
GET /api/users
Accept: application/vnd.myapi.v2+json

// 3. Query Parameter (not recommended for APIs)
GET /api/users?version=2

非推奨を示すヘッダー:

// Communicate deprecation
res.setHeader('Deprecation', 'true');
res.setHeader('Sunset', 'Sat, 01 Jun 2025 00:00:00 GMT');
res.setHeader('Link', '</api/v2/users>; rel="successor-version"');
リクエスト/レスポンスの設計

よくある問題:

  • フィールド名に一貫性がない(camelCase と snake_case)
  • Content-Type ヘッダーが欠落している
  • リクエストの検証がない
  • レスポンスが過度に冗長

リクエスト/レスポンスのベストプラクティス:

// Consistent naming convention (pick one, stick to it)
// JavaScript/TypeScript typically uses camelCase

// Request validation with Zod
import { z } from 'zod';

const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2).max(100),
  age: z.number().int().min(18).optional(),
  role: z.enum(['user', 'admin']).default('user'),
});

// Validate in middleware
function validate(schema: z.ZodSchema) {
  return (req: Request, res: Response, next: NextFunction) => {
    try {
      req.body = schema.parse(req.body);
      next();
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({
          status: 400,
          code: 'VALIDATION_ERROR',
          message: 'Request validation failed',
          details: error.errors.map(e => ({
            field: e.path.join('.'),
            message: e.message,
            code: e.code,
          })),
        });
      }
      next(error);
    }
  };
}

// Response envelope for consistency
interface ApiResponse<T> {
  data: T;
  meta?: {
    pagination?: PaginationInfo;
    [key: string]: any;
  };
}

// Partial responses (field selection)
GET /users/123?fields=id,name,email

コードレビューのチェックリスト

エンドポイントの設計
  • URL には動詞ではなく名詞を使用している
  • 命名規則(kebab-case または snake_case)に一貫性がある
  • リソース階層が適切である
  • リソースを深くネストしすぎていない(最大 2 階層)
HTTP のセマンティクス
  • 操作に適した HTTP メソッドを使用している
  • 適切なステータスコードを使用している
  • PUT/DELETE で冪等性を確保している
  • 安全なメソッド(GET)で状態を変更しない
エラー処理
  • エラーレスポンス形式に一貫性がある
  • エラーコードに意味がある
  • 検証エラーにフィールドの詳細が含まれている
  • 内部エラーをクライアントに公開していない
パフォーマンス
  • リストエンドポイントでページネーションを使用している
  • フィールド選択に対応している
  • 適切なキャッシュヘッダーを使用している
  • レート制限を実装している
ドキュメント
  • OpenAPI/Swagger 仕様が最新である
  • すべてのエンドポイントに例がある
  • エラーコードを文書化している
  • 古いバージョンに非推奨の警告がある

避けるべきアンチパターン

  1. RPC 形式の URL:/createUser、/updateProduct → HTTP メソッドと名詞を使用します
  2. HTTP セマンティクスの無視:すべてに POST を使用する
  3. 内部 ID の公開:自動採番 ID の代わりに UUID または不透明な ID を使用します
  4. 過剰なデータ取得:要求された、または必要なフィールドのみを返します
  5. レスポンスボディにバージョンを含める:URL にバージョンを含める方が明確です
  6. 密結合:API はフロントエンドの実装から独立させます

コメント