api-endpoint-builder について
バックエンド開発者向けに、レビューしやすい REST API エンドポイントのひな形を作るスキルです。入力検証、認証・認可、エラー処理、レスポンス形式、文書化、任意のテストを扱います。
主な機能
- 01ルートハンドラーのひな形
- 02リクエスト入力の検証
- 03認証・認可チェック
- 04一貫したエラーとレスポンス
- 05エンドポイントの文書化とテスト
活用シーン
- 01API に CRUD エンドポイントを追加
- 02リクエスト本文やパラメーターを検証
- 03アクセスチェックでルートを保護
- 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 にマッピングしてください。事前チェックだけでは不十分です。認証済みのテナント/リソースフィルターを、行のクエリと件数クエリの両方に適用し、レスポンスフィールドとソート/フィルター入力を許可リストで制限し、エラーログをサニタイズしてください。本番環境に移行する前に、文字列以外の本文、認可、重複登録が同時に発生するケースをテストしてください。
コメント
Markdown 対応