mcpField
A
api-endpoint-builder

api-endpoint-builder

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

api-endpoint-builder について

バックエンド開発者向けに、レビューしやすい REST API エンドポイントのひな形を作るスキルです。入力検証、認証・認可、エラー処理、レスポンス形式、文書化、任意のテストを扱います。

主な機能

  1. 01ルートハンドラーのひな形
  2. 02リクエスト入力の検証
  3. 03認証・認可チェック
  4. 04一貫したエラーとレスポンス
  5. 05エンドポイントの文書化とテスト

活用シーン

  1. 01API に CRUD エンドポイントを追加
  2. 02リクエスト本文やパラメーターを検証
  3. 03アクセスチェックでルートを保護
  4. 04重要なルートの文書とテストを作成

インストール

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

$npx skills add https://github.com/sickn33/agentic-awesome-skills --skill api-endpoint-builder

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

同梱ファイルREADME.md

詳細

API エンドポイントビルダー

適切なバリデーション、エラー処理、認証、ドキュメントを備えた、完全でレビュー可能な REST API エンドポイントのひな形を作成します。

このスキルを使う場合

  • ユーザーが「API エンドポイントを作成して」または「REST API を構築して」と依頼した場合
  • 新しいバックエンド機能を構築する場合
  • 既存の API にエンドポイントを追加する場合
  • ユーザーが「API」、「エンドポイント」、「ルート」、「REST」に言及した場合
  • CRUD 操作を作成する場合

作成するもの

各エンドポイントについて、次のものを作成します。

  • 適切な HTTP メソッドを使用するルートハンドラー
  • 入力バリデーション(リクエスト本文、パラメーター、クエリ)
  • 認証/認可チェック
  • ビジネスロジック
  • エラー処理
  • レスポンス形式
  • API ドキュメント
  • テスト(依頼された場合)

エンドポイントの構造

1. ルート定義
// Express example
router.post('/api/users', authenticate, validateUser, createUser);

// Fastify example
fastify.post('/api/users', {
  preHandler: [authenticate],
  schema: userSchema
}, createUser);
2. 入力バリデーション

処理の前に必ずバリデーションを行います。

const validateUser = (req, res, next) => {
  const { email, name, password } = req.body ?? {};
  
  if (typeof email !== 'string' || !email.includes('@')) {
    return res.status(400).json({ error: 'Valid email required' });
  }
  
  if (typeof name !== 'string' || name.trim().length < 2) {
    return res.status(400).json({ error: 'Name must be at least 2 characters' });
  }
  
  if (typeof password !== 'string' || password.length < 8) {
    return res.status(400).json({ error: 'Password must be at least 8 characters' });
  }
  
  next();
};
3. ハンドラーの実装
const createUser = async (req, res) => {
  try {
    const { email, name, password } = req.body ?? {};
    
    // Check if user exists
    const existing = await db.users.findOne({ email });
    if (existing) {
      return res.status(409).json({ error: 'User already exists' });
    }
    
    // Hash password
    const hashedPassword = await bcrypt.hash(password, 10);
    
    // Create user
    const user = await db.users.create({
      email,
      name,
      password: hashedPassword,
      createdAt: new Date()
    });
    
    // Don't return password
    const userWithoutPassword = { id: user.id, email: user.email, name: user.name };
    
    res.status(201).json({
      success: true,
      data: userWithoutPassword
    });
    
  } catch (error) {
    console.error('Create user error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
};

ベストプラクティス

HTTP ステータスコード
  • 200 - 成功(GET、PUT、PATCH)
  • 201 - 作成済み(POST)
  • 204 - コンテンツなし(DELETE)
  • 400 - 不正なリクエスト(バリデーション失敗)
  • 401 - 認証されていない(未認証)
  • 403 - 禁止(認可されていない)
  • 404 - 見つからない
  • 409 - 競合(重複)
  • 500 - 内部サーバーエラー
レスポンス形式

一貫性のある構造にします。

// Success
{
  "success": true,
  "data": { ... }
}

// Error
{
  "error": "Error message",
  "details": { ... } // optional
}

// List with pagination
{
  "success": true,
  "data": [...],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 100
  }
}
セキュリティチェックリスト
  • 保護されたルートでは認証を必須にする
  • 認可チェックを行う(ユーザーがリソースを所有しているか)
  • すべてのフィールドで入力バリデーションを行う
  • SQL インジェクションを防止する(パラメーター化クエリを使用する)
  • 公開エンドポイントにレート制限を設定する
  • レスポンスに機密データを含めない(パスワード、トークンなど)
  • CORS を適切に設定する
  • リクエストサイズの上限を設定する
エラー処理
// Centralized error handler
app.use((err, req, res, next) => {
  console.error(err.stack);
  
  // Don't leak error details in production
  const message = process.env.NODE_ENV === 'production' 
    ? 'Internal server error' 
    : err.message;
  
  res.status(err.status || 500).json({ error: message });
});

よくあるパターン

CRUD 操作
// Create
POST /api/resources
Body: { name, description }

// Read (list)
GET /api/resources?page=1&limit=20

// Read (single)
GET /api/resources/:id

// Update
PUT /api/resources/:id
Body: { name, description }

// Delete
DELETE /api/resources/:id
ページネーション
const getResources = async (req, res) => {
  const page = Number(req.query.page ?? 1);
  const limit = Number(req.query.limit ?? 20);
  if (!Number.isSafeInteger(page) || page < 1 || !Number.isSafeInteger(limit) || limit < 1 || limit > 100 || (page - 1) * limit > 100000) {
    return res.status(400).json({ error: "Invalid pagination" });
  }
  const skip = (page - 1) * limit;
  
  const [resources, total] = await Promise.all([
    db.resources.find().skip(skip).limit(limit),
    db.resources.countDocuments()
  ]);
  
  res.json({
    success: true,
    data: resources,
    pagination: {
      page,
      limit,
      total,
      pages: Math.ceil(total / limit)
    }
  });
};
フィルタリングとソート
const getResources = async (req, res) => {
  const { status, sort = '-createdAt' } = req.query;
  
  const filter = {};
  if (status && !["active", "inactive"].includes(status)) return res.status(400).json({ error: "Invalid status" });
  if (!["createdAt", "-createdAt", "name", "-name"].includes(sort)) return res.status(400).json({ error: "Invalid sort" });
  if (status) filter.status = status;
  
  const resources = await db.resources
    .find(filter)
    .sort(sort)
    .limit(20);
  
  res.json({ success: true, data: resources });
};

ドキュメントのテンプレート

/**
 * @route POST /api/users
 * @desc Create a new user
 * @access Public
 * 
 * @body {string} email - User email (required)
 * @body {string} name - User name (required)
 * @body {string} password - Password, min 8 chars (required)
 * 
 * @returns {201} User created successfully
 * @returns {400} Validation error
 * @returns {409} User already exists
 * @returns {500} Server error
 * 
 * @example
 * POST /api/users
 * {
 *   "email": "[email protected]",
 *   "name": "John Doe",
 *   "password": "securepass123"
 * }
 */

テストの例

describe('POST /api/users', () => {
  it('should create a new user', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({
        email: '[email protected]',
        name: 'Test User',
        password: 'password123'
      });
    
    expect(response.status).toBe(201);
    expect(response.body.success).toBe(true);
    expect(response.body.data.email).toBe('[email protected]');
    expect(response.body.data.password).toBeUndefined();
  });
  
  it('should reject invalid email', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({
        email: 'invalid',
        name: 'Test User',
        password: 'password123'
      });
    
    expect(response.status).toBe(400);
    expect(response.body.error).toContain('email');
  });
});

重要な原則

  • 処理の前にすべての入力を検証する
  • 適切な HTTP ステータスコードを使用する
  • エラーを適切に処理する
  • 機密データを決して公開しない
  • レスポンスに一貫性を持たせる
  • 必要に応じて認証を追加する
  • エンドポイントをドキュメント化する
  • 重要な処理経路のテストを作成する

関連スキル

  • @security-auditor - セキュリティレビュー
  • @test-driven-development - テスト
  • @database-design - データモデリング

制限事項

  • タスクが上記の説明範囲に明確に合致する場合に限り、このスキルを使用してください。
  • 出力を、環境固有の検証、テスト、または専門家によるレビューの代わりとして扱わないでください。
  • 必要な入力、権限、安全上の境界、または成功基準が不足している場合は、作業を止めて確認してください。

これらの断片を使用するには、実際のデータベース/モデル、認証ミドルウェア、アプリケーションのセットアップが必要です。例示的な @ チェックだけに頼らず、スキーマバリデーションでメールアドレスとパスワードの長さ、およびバイト数の上限を検証してください。正規化したメールアドレスの一意性をデータベースで強制し、重複キーによる競合状態を 409 にマッピングしてください。事前チェックだけでは不十分です。認証済みのテナント/リソースフィルターを、行のクエリと件数クエリの両方に適用し、レスポンスフィールドとソート/フィルター入力を許可リストで制限し、エラーログをサニタイズしてください。本番環境に移行する前に、文字列以外の本文、認可、重複登録が同時に発生するケースをテストしてください。

コメント