mcpField
R
rest-api

rest-api

github.com/claude-dev-suite開発4647 回インストール6 日前に更新
リポジトリ

rest-api について

REST API の設計と運用に取り組む開発者向けのリファレンススキルです。リソースの規約やステータスコードに加え、検証、ページネーション、セキュリティを扱います。

主な機能

  1. 01リソース命名と HTTP メソッド
  2. 02ステータスコードとレスポンス形式
  3. 03クエリパラメーターとページネーション
  4. 04検証、セキュリティ、API バージョニング

活用シーン

  1. 01一貫性のある REST エンドポイントの設計
  2. 02ページネーションと API バージョンの計画
  3. 03HTTP エラーや API 問題の診断

MCP 対応

より包括的な REST API ドキュメントを取得するための MCP ドキュメントツールに言及しています。

インストール

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

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill rest-api

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

同梱ファイルquick-ref/conventions.mdquick-ref/error-handling.md

詳細

REST API の基本知識

詳しい知識:包括的なドキュメントについては、技術として rest-api を指定して mcp__documentation__fetch_docs を使用してください。

リソースの命名

# Collections (plural nouns)
GET    /users              # List users
POST   /users              # Create user
GET    /users/{id}         # Get user
PUT    /users/{id}         # Update user
DELETE /users/{id}         # Delete user

# Nested resources
GET    /users/{id}/posts   # User's posts
POST   /users/{id}/posts   # Create user's post

# Avoid verbs in URLs
❌ GET /getUsers
❌ POST /createUser
✅ GET /users
✅ POST /users

HTTP メソッド

メソッド目的冪等性
GETリソースの読み取りはい
POSTリソースの作成いいえ
PUTリソースの置き換えはい
PATCH部分更新はい
DELETEリソースの削除はい

ステータスコード

コード意味用途
200OKGET/PUT/PATCH の成功
201作成済みPOST の成功
204コンテンツなしDELETE の成功
400不正なリクエスト検証エラー
401認証されていません認証情報がない、または無効
403禁止されています権限がありません
404見つかりませんリソースが存在しません
409競合重複/競合
422処理できません意味上のエラー
500サーバーエラー内部エラー

レスポンス形式

// Success
{
  "data": { "id": 1, "name": "John" },
  "meta": { "timestamp": "2024-01-15T10:00:00Z" }
}

// Collection
{
  "data": [...],
  "meta": { "total": 100, "page": 1, "limit": 20 }
}

// Error
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Email is required",
    "details": [{ "field": "email", "message": "Required" }]
  }
}

クエリパラメーター

GET /users?status=active&sort=-createdAt&page=1&limit=20
GET /users?fields=id,name,email
GET /users?include=posts,profile
GET /users?filter[age][gte]=18

このスキルを使用すべきでない場合

  • GraphQL API の設計(graphql スキルを使用)
  • tRPC による型安全な API(trpc スキルを使用)
  • OpenAPI 仕様の記述(openapi スキルを使用)
  • WebSocket または SSE を必要とするリアルタイム API
  • 複雑なネストされたクエリを必要とする API(GraphQL を検討)

アンチパターン

アンチパターン問題点解決策
URL に動詞を使用する(/getUser、/createOrder)REST の原則に沿わず、リソース命名規則に違反します名詞に対して HTTP メソッドを使用します(GET /users、POST /orders)
エラーに対して 200 を返す誤解を招き、HTTP のセマンティクスに反します適切な 4xx/5xx ステータスコードを使用します
API のバージョン管理をしない破壊的変更がすべてのクライアントに影響しますURL またはヘッダーでバージョンを管理します(/v1/、/v2/)
データベース ID をそのまま公開するセキュリティリスクとなり、実装が漏えいしますUUID または不透明な識別子を使用します
大規模なコレクションにページネーションがないパフォーマンスの問題やタイムアウトが発生しますカーソルまたはオフセット方式のページネーションを実装します
HTTP キャッシュヘッダーを無視するパフォーマンスが低下し、不要な負荷が発生しますETag、Cache-Control、Last-Modified を使用します
状態を変更する操作に GET を使用するセキュリティリスクとなり、REST の原則に反しますPOST、PUT、PATCH、DELETE を使用します
レスポンス形式に一貫性がないクライアントが混乱し、統合に問題が生じますJSON エンベロープ形式に標準化します
レート制限がないAPI の悪用やリソース枯渇につながります429 レスポンスを返すレート制限を実装します

トラブルシューティングの概要

問題考えられる原因解決策
401 認証されていません認証トークンがない、または無効ですAuthorization ヘッダーを確認し、トークンを検証します
403 禁止されています認証は有効ですが、権限が不足していますユーザーのロールを確認し、アクセス制御を検証します
404 見つかりませんリソースが存在しないか、パスが正しくありませんエンドポイント URL を確認し、リソース ID を確認します
409 競合リソースがすでに存在するか、状態が競合しています一意性制約を確認し、冪等性を適切に処理します
422 処理できないエンティティ検証に失敗しましたリクエストボディを確認し、スキーマに照らして検証します
429 リクエストが多すぎますレート制限を超過しましたバックオフを実装し、レート制限ヘッダーを確認します
500 内部サーバーエラーサーバー側のバグまたはクラッシュですサーバーログを確認し、エラーハンドリングを追加します
CORS エラーCORS ヘッダーがありません許可するオリジンを指定して CORS ミドルウェアを設定します
API のレスポンスが遅いページネーションがない、または N+1 クエリが発生していますページネーションを追加し、データベースクエリを最適化します

本番環境への対応

セキュリティ設定
// Input validation and sanitization
import { z } from 'zod';

const CreateUserSchema = z.object({
  name: z.string().min(1).max(100).trim(),
  email: z.string().email().toLowerCase(),
  age: z.number().int().min(0).max(150).optional(),
});

// Validate request body
app.post('/users', async (req, res) => {
  const result = CreateUserSchema.safeParse(req.body);
  if (!result.success) {
    return res.status(400).json({
      error: { code: 'VALIDATION_ERROR', details: result.error.issues },
    });
  }
  // Use result.data (validated and transformed)
});

// SQL injection prevention - always use parameterized queries
// NEVER: `SELECT * FROM users WHERE id = '${userId}'`
// GOOD: Use ORM or prepared statements
レート制限
import rateLimit from 'express-rate-limit';

// Different limits for different endpoints
const standardLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100,
  standardHeaders: true,
  message: { error: { code: 'RATE_LIMIT_EXCEEDED' } },
});

const authLimiter = rateLimit({
  windowMs: 60 * 60 * 1000, // 1 hour
  max: 5, // Strict for auth endpoints
  skipSuccessfulRequests: true,
});

app.use('/api', standardLimiter);
app.use('/api/auth/login', authLimiter);
API のバージョン管理
// URL versioning (recommended for breaking changes)
app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);

// Header versioning (for minor versions)
app.use('/api', (req, res, next) => {
  const version = req.headers['api-version'] || '1.0';
  req.apiVersion = version;
  next();
});

// Sunset header for deprecated endpoints
app.get('/api/v1/legacy', (req, res) => {
  res.set('Sunset', 'Sat, 01 Jun 2025 00:00:00 GMT');
  res.set('Deprecation', 'true');
  // ... handle request
});
ページネーション
// Cursor-based pagination (recommended for large datasets)
interface PaginatedResponse<T> {
  data: T[];
  meta: {
    nextCursor: string | null;
    hasMore: boolean;
  };
}

app.get('/users', async (req, res) => {
  const cursor = req.query.cursor as string | undefined;
  const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);

  const users = await db.users.findMany({
    take: limit + 1, // Fetch one extra to check if there's more
    cursor: cursor ? { id: cursor } : undefined,
    orderBy: { createdAt: 'desc' },
  });

  const hasMore = users.length > limit;
  const data = hasMore ? users.slice(0, -1) : users;

  res.json({
    data,
    meta: {
      nextCursor: hasMore ? data[data.length - 1].id : null,
      hasMore,
    },
  });
});
エラーハンドリング
// Consistent error response format
interface APIError {
  error: {
    code: string;
    message: string;
    details?: unknown;
    requestId?: string;
  };
}

// Global error handler
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  const requestId = req.headers['x-request-id'] as string;

  // Log error with context
  logger.error({
    err,
    requestId,
    path: req.path,
    method: req.method,
  });

  // Don't leak internal errors
  const statusCode = err instanceof HTTPError ? err.statusCode : 500;
  const message = statusCode === 500 ? 'Internal Server Error' : err.message;

  res.status(statusCode).json({
    error: {
      code: err.code || 'INTERNAL_ERROR',
      message,
      requestId,
    },
  });
});
監視メトリクス
メトリクスアラートしきい値
リクエストレイテンシ p99> 500ms
エラー率(4xx)> 5%
エラー率(5xx)> 1%
レート制限のヒット数> 100/min
リクエストサイズ> 10MB
CORS 設定
import cors from 'cors';

const corsOptions = {
  origin: (origin, callback) => {
    const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || [];
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-ID'],
  credentials: true,
  maxAge: 86400, // 24 hours
};

app.use(cors(corsOptions));
リクエスト/レスポンスのログ記録
import pino from 'pino-http';

app.use(pino({
  redact: ['req.headers.authorization', 'req.body.password'],
  serializers: {
    req: (req) => ({
      method: req.method,
      url: req.url,
      query: req.query,
    }),
    res: (res) => ({
      statusCode: res.statusCode,
    }),
  },
}));
チェックリスト
  • すべてのエンドポイントで入力を検証する
  • レート制限を設定する
  • CORS を適切に制限する
  • API バージョニング戦略を策定する
  • カーソルベースのページネーションを使用する
  • エラー形式を統一する
  • リクエスト ID を追跡する
  • 構造化ログを記録する
  • URL に機密データを含めない
  • リクエストボディのサイズ制限を設定する
  • タイムアウトを処理する
  • ヘルスチェックエンドポイントを用意する

リファレンスドキュメント

コメント