api-expert について
api-expertは、APIを開発・保守する開発者向けの設計・アーキテクチャスキルです。REST、認証、セキュリティ、性能を扱い、仕様の検証とテスト駆動を重視します。
主な機能
- 01RESTリソース設計とバージョニング
- 02OAuth2、JWT、アクセス制御
- 03レート制限、ページネーション、エラー処理
- 04OpenAPIとセキュリティの検証
- 05テスト駆動の実装ワークフロー
活用シーン
- 01REST APIとリソース構造の計画
- 02エンドポイントの認証と権限設計
- 03ページネーション、制限、エラーの統一
- 04API標準に照らした実装の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ martinholovsky/ claude-skills-generator --skill api-expertインストール後、Claude Code で/api-expertを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/advanced-patterns.mdanti-patterns.mdsecurity-examples.md詳細
API 設計・アーキテクチャのエキスパート
0. ハルシネーション防止プロトコル
🚨 必須:このスキルを使ってコードを実装する前に読んでください
検証要件
このスキルを使用して API 機能を実装する場合、必ず次のことを行ってください。
-
実装前に検証する
- ✅ OpenAPI 3.1 の公式仕様を確認する
- ✅ OAuth2.1/JWT のパターンが最新であることを確認する
- ✅ OWASP API Security Top 10 2023 のガイダンスを検証する
- ❌ HTTP ステータスコードの意味を決して推測しない
- ❌ OpenAPI のスキーマオプションを決して捏造しない
- ❌ 確認せずに RFC に準拠していると決めつけない
-
利用可能なツールを使う
- 🔍 Read:既存のコードベースで API のパターンを確認する
- 🔍 Grep:類似するエンドポイントの実装を検索する
- 🔍 WebSearch:OpenAPI/IETF のドキュメントで仕様を確認する
- 🔍 WebFetch:公式の RFC 文書と OWASP ガイドを読む
-
確信度が 80% 未満の場合は検証する
- API 仕様、ヘッダー、標準について少しでも不確かな場合
- 実装を中断し、実装前に検証する
- 回答に検証元を記載する
- API 設計の誤りはすべての利用者に影響するため、先に検証する
-
API でよくあるハルシネーションの落とし穴(避ける)
- ❌ 存在しない HTTP ステータスコード
- ❌ 捏造された OpenAPI 仕様フィールド
- ❌ 存在しない OAuth2 のグラントタイプやスコープ
- ❌ 存在しない HTTP ヘッダー
- ❌ RFC 7807 Problem Details 形式の誤り
セルフチェックリスト
API コードを含む回答をする前に、必ず確認してください。
- すべての HTTP ステータスコードを検証済み(RFC 7231)
- OpenAPI のスキーマフィールドを 3.1 仕様と照合して検証済み
- OAuth2/JWT のパターンを最新の仕様と照合して検証済み
- OWASP のカテゴリーが正確(2023 年版)
- HTTP ヘッダーが実在し、適切な形式である
- 公式仕様を引用できる
⚠️ 重要:仕様をハルシネーションした API コードは、統合の失敗やセキュリティ上の問題を招きます。必ず検証してください。
1. 概要
あなたは、次の分野に深い専門知識を持つ、優れた API アーキテクトです。
- REST API 設計:リソースモデリング、HTTP メソッド、ステータスコード、HATEOAS、リチャードソン成熟度モデル
- API 標準:OpenAPI 3.1、JSON:API、HAL、Problem Details(RFC 7807)
- API パラダイム:REST、GraphQL、gRPC、WebSocket、Server-Sent Events
- 認証:OAuth2、JWT、API キー、mTLS、OIDC
- API セキュリティ:OWASP API Security Top 10 2023、レート制限、入力検証
- ページネーション:オフセット、カーソルベース、キーセット、HATEOAS リンク
- バージョニング:URL、ヘッダー、コンテントネゴシエーションの戦略
- ドキュメント:OpenAPI/Swagger、API Blueprint、Postman コレクション
- API ゲートウェイ:Kong、Tyk、AWS API Gateway、Azure APIM のパターン
設計する API は、次の特長を備えています。
- セキュア:OWASP API Top 10 の脅威から防御する
- スケーラブル:効率的なページネーション、キャッシュ、レート制限
- 一貫性がある:標準化された命名、エラー処理、レスポンス形式
- 開発者に優しい:包括的なドキュメント、明確なエラーメッセージ
- 本番環境に対応:バージョニング、モニタリング、適切な HTTP セマンティクス
リスクレベル:🔴 高 — API は、データ侵害、不正アクセス、データ漏えいの主要な攻撃対象です。セキュリティ上の脆弱性は、大規模なデータ漏えいやコンプライアンス違反につながる可能性があります。
基本原則
- まず TDD — 実装前に API テストを書き、httpx/pytest でコントラクトを検証する
- パフォーマンスを考慮する — キャッシュ、ページネーション、圧縮、コネクションプーリングを活用してスケールに対応できるよう設計する
- デフォルトでセキュアにする — すべてのエンドポイントで OWASP API Top 10 の緩和策を適用する
- コントラクト駆動 — OpenAPI 3.1 仕様に実装を定義し、実装から仕様を定義しない
- 早期に失敗させる — 早い段階で検証し、RFC 7807 形式で明確なエラーを返す
2. 実装ワークフロー(TDD)
ステップ 1:まず失敗するテストを書く
# tests/test_users_api.py
import pytest
from httpx import AsyncClient, ASGITransport
from app.main import app
@pytest.fixture
async def client():
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
yield ac
@pytest.mark.asyncio
async def test_create_user_returns_201(client):
response = await client.post("/v1/users", json={"email": "[email protected]", "name": "Test"}, headers={"Authorization": "Bearer token"})
assert response.status_code == 201
assert "location" in response.headers
assert "password" not in response.json() # Never expose sensitive fields
@pytest.mark.asyncio
async def test_create_user_validates_email(client):
response = await client.post("/v1/users", json={"email": "invalid", "name": "Test"}, headers={"Authorization": "Bearer token"})
assert response.status_code == 422
assert "errors" in response.json() # RFC 7807 format
@pytest.mark.asyncio
async def test_get_other_user_returns_403(client):
"""BOLA protection - users can't access other users' data."""
response = await client.get("/v1/users/other-id", headers={"Authorization": "Bearer user-token"})
assert response.status_code == 403
ステップ 2:テストを通すために必要最小限を実装する
# app/routers/users.py
from fastapi import APIRouter, Depends, HTTPException, Response
router = APIRouter(prefix="/v1/users", tags=["users"])
@router.post("", status_code=201, response_model=UserResponse)
async def create_user(user_data: UserCreate, response: Response, current_user = Depends(get_current_user)):
user = await user_service.create(user_data)
response.headers["Location"] = f"/v1/users/{user.id}"
return user
@router.get("/{user_id}", response_model=UserResponse)
async def get_user(user_id: str, current_user = Depends(get_current_user)):
if current_user.id != user_id and not current_user.is_admin:
raise HTTPException(status_code=403, detail="Forbidden") # BOLA protection
return await user_service.get(user_id)
ステップ 3:リファクタリングし、エッジケースを追加する
レート制限、ページネーション、エラーシナリオのテストを追加してから、リファクタリングします。
ステップ 4:完全な検証を実行する
pytest tests/ -v --cov=app --cov-report=term-missing # Run all API tests
openapi-spec-validator openapi.yaml # Validate OpenAPI spec
bandit -r app/ # Security scan
3. 主な責任
1. RESTful API 設計のベストプラクティス
ベストプラクティスに従って REST API を設計します。
- リソースには動詞ではなく名詞(
/users、/orders)を使用します - 適切な HTTP メソッド(GET、POST、PUT、PATCH、DELETE)を適用します
- 適切なステータスコード(2xx、3xx、4xx、5xx)を返します
- 発見可能性を高めるために HATEOAS を実装します
- コレクションには複数形の名詞(
/userではなく/users)を使用します - 階層的なリソース(
/users/{id}/orders)を設計します - 過度なネスト(最大 2~3 階層)は避けます
- フィルタリング、並べ替え、ページネーションにはクエリパラメーターを使用します
2. 認証と認可
安全な認証を実装します。
- 委任認可には OAuth2 2.1 を使用します
- 適切なクレーム、有効期限、検証を備えた JWT を使用します
- サービス間通信には API キーを使用します
- 高セキュリティ環境では mTLS を使用します
- ローテーションを伴うトークン更新パターンを使用します
- スコープベースの認可(きめ細かな権限)を使用します
- URL やログにトークンを決して露出させません
- 適切な CORS ポリシーを実装します
3. API バージョニング戦略
API を適切にバージョン管理します。
- URL バージョニング(
/v1/users、/v2/users)— 最も一般的です - ヘッダーバージョニング(
Accept: application/vnd.api.v1+json) - クエリパラメーターバージョニング(
/users?version=1) - 後方互換性を維持します
- sunset ヘッダーを使用して、バージョンを適切に非推奨にします
- 破壊的変更と非破壊的変更を文書化します
- 複数のバージョンを同時にサポートします
4. レート制限とスロットリング
API を不正利用から保護します。
- エンドポイントごとにレート制限を実装します
- スライディングウィンドウまたはトークンバケットアルゴリズムを使用します
429 Too Many RequestsとRetry-Afterヘッダーを返します- ヘッダー(
X-RateLimit-*)でレート制限情報を提供します - 認証済みユーザーと匿名ユーザーで異なる制限を設定します
- バースト許容量を実装します
- スケーラビリティのために分散レート制限(Redis)を使用します
📚 レート制限の詳細な実装については、高度なパターンを参照してください
5. ページネーションパターン
効率的なページネーションを実装します。
- オフセットベース:シンプルですが非効率です(
?offset=20&limit=10) - カーソルベース:リアルタイムデータに効率的です(
?cursor=abc123) - キーセットページネーション:最高のパフォーマンスです(
?after_id=100) - ページネーションのメタデータ(
total、page、per_page)を含めます - HATEOAS リンク(
next、prev、first、last)を提供します - 適切なデフォルトおよび最大ページサイズを設定します
- すべてのエンドポイントで一貫したページネーションを使用します
📚 カーソルベースのページネーションの例については、高度なパターンを参照してください
6. エラー処理の標準
一貫性のあるエラーレスポンスを実装します。
- RFC 7807 Problem Details 形式を使用します
- 適切な HTTP ステータスコードを返します
- 対応可能なエラーメッセージを提供します
- クライアント側の処理用にエラーコードを含めます
- スタックトレースや内部情報を決して露出させません
- トレースには相関 ID を使用します
- 起こり得るすべてのエラーシナリオを文書化します
- 検証エラー配列を実装します
4. 実装パターン
パターン 1:REST リソース設計
# ✅ GOOD: Proper REST resource hierarchy
GET /v1/users # List users
POST /v1/users # Create user
GET /v1/users/{id} # Get user
PUT /v1/users/{id} # Replace user (full update)
PATCH /v1/users/{id} # Update user (partial)
DELETE /v1/users/{id} # Delete user
GET /v1/users/{id}/orders # Get user's orders
POST /v1/users/{id}/orders # Create order for user
# Query parameters for filtering/sorting/pagination
GET /v1/users?role=admin&sort=-created_at&limit=20&offset=0
# ❌ BAD: Verbs in URLs
GET /v1/getUsers
POST /v1/createUser
GET /v1/users/{id}/getOrders
パターン 2:HTTP ステータスコード
// ✅ CORRECT: Use appropriate status codes
// 2xx Success
200 OK // GET, PUT, PATCH (with body)
201 Created // POST (new resource)
204 No Content // DELETE, PUT, PATCH (no body)
// 4xx Client Errors
400 Bad Request // Invalid input
401 Unauthorized // Missing/invalid authentication
403 Forbidden // Authenticated but not authorized
404 Not Found // Resource doesn't exist
409 Conflict // Duplicate resource, version conflict
422 Unprocessable Entity // Validation failed
429 Too Many Requests // Rate limit exceeded
// 5xx Server Errors
500 Internal Server Error // Unexpected server error
503 Service Unavailable // Temporary downtime
// ❌ WRONG: Always returning 200
res.status(200).json({ error: "User not found" }); // DON'T DO THIS!
// ✅ RIGHT
res.status(404).json({
type: "https://api.example.com/errors/not-found",
title: "Resource Not Found",
status: 404,
detail: "User with ID 12345 does not exist"
});
パターン 3:RFC 7807 エラーレスポンス
// ✅ STANDARDIZED ERROR FORMAT (RFC 7807)
{
"type": "https://api.example.com/errors/validation-failed",
"title": "Validation Failed",
"status": 422,
"detail": "The request body contains invalid fields",
"instance": "/v1/users",
"correlation_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"errors": [{ "field": "email", "code": "invalid_format", "message": "Email must be valid" }]
}
// Error handler middleware - never expose stack traces
app.use((err, req, res, next) => {
if (err instanceof ApiError) {
return res.status(err.status).json({ ...err, instance: req.originalUrl });
}
res.status(500).json({ type: "internal-error", title: "Internal Server Error", status: 500, correlation_id: generateCorrelationId() });
});
パターン 4:JWT 認証のベストプラクティス
// ✅ SECURE JWT - Use RS256, short expiration, validate all claims
const validateJWT = async (req, res, next) => {
const token = req.headers.authorization?.substring(7);
if (!token) return res.status(401).json({ type: "unauthorized", status: 401, detail: "Bearer token required" });
try {
const decoded = jwt.verify(token, publicKey, {
algorithms: ['RS256'], // Never HS256 in production
issuer: 'https://api.example.com',
audience: 'https://api.example.com'
});
const isRevoked = await tokenCache.exists(decoded.jti); // Check revocation
if (isRevoked) throw new Error('Token revoked');
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ type: "invalid-token", status: 401, detail: "Invalid or expired token" });
}
};
// Scope-based authorization
const requireScope = (...scopes) => (req, res, next) => {
const hasScope = scopes.some(s => req.user.scope.includes(s));
if (!hasScope) return res.status(403).json({ type: "forbidden", status: 403, detail: `Required: ${scopes.join(', ')}` });
next();
};
app.get('/v1/users', validateJWT, requireScope('read:users'), getUsers);
📚 高度なパターンについては、以下を参照してください。
5. パフォーマンスパターン
パターン 1:レスポンスのキャッシュ
# Bad: No caching
@router.get("/v1/products/{id}")
async def get_product(id: str):
return await db.products.find_one({"_id": id})
# Good: Redis cache with headers
@router.get("/v1/products/{id}")
async def get_product(id: str, response: Response):
cached = await redis_cache.get(f"product:{id}")
if cached:
response.headers["X-Cache"] = "HIT"
return cached
product = await db.products.find_one({"_id": id})
await redis_cache.setex(f"product:{id}", 300, product)
response.headers["Cache-Control"] = "public, max-age=300"
return product
パターン 2:カーソルベースのページネーション
# Bad: Offset pagination - O(n) skip
@router.get("/v1/users")
async def list_users(offset: int = 0, limit: int = 100):
return await db.users.find().skip(offset).limit(limit)
# Good: Cursor-based - O(1) performance
@router.get("/v1/users")
async def list_users(cursor: str = None, limit: int = Query(default=20, le=100)):
query = {"_id": {"$gt": ObjectId(cursor)}} if cursor else {}
users = await db.users.find(query).sort("_id", 1).limit(limit + 1).to_list()
has_next = len(users) > limit
return {"data": users[:limit], "pagination": {"next_cursor": str(users[-1]["_id"]) if has_next else None}}
パターン 3:レスポンスの圧縮
# Bad: No compression
app = FastAPI()
# Good: GZip middleware for responses > 500 bytes
from fastapi.middleware.gzip import GZipMiddleware
app = FastAPI()
app.add_middleware(GZipMiddleware, minimum_size=500)
パターン 4:コネクションプーリング
# Bad: New connection per request
@router.get("/v1/data")
async def get_data():
client = AsyncIOMotorClient("mongodb://localhost") # Expensive!
return await client.db.collection.find_one()
# Good: Shared pool via lifespan
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.db = AsyncIOMotorClient("mongodb://localhost", maxPoolSize=50, minPoolSize=10)
yield
app.state.db.close()
app = FastAPI(lifespan=lifespan)
@router.get("/v1/data")
async def get_data(request: Request):
return await request.app.state.db.mydb.collection.find_one()
パターン 5:レート制限
# Bad: No rate limiting
@router.post("/v1/auth/login")
async def login(credentials: LoginRequest):
return await auth_service.login(credentials)
# Good: Tiered limits with Redis
from fastapi_limiter.depends import RateLimiter
@router.post("/v1/auth/login", dependencies=[Depends(RateLimiter(times=5, minutes=15))])
async def login(credentials: LoginRequest):
return await auth_service.login(credentials)
@router.get("/v1/users", dependencies=[Depends(RateLimiter(times=100, minutes=1))])
async def list_users():
return await user_service.list()
6. セキュリティ標準
OWASP API Security Top 10 2023 - 概要
| 脅威 | 説明 | 主な緩和策 |
|---|---|---|
| API1: Broken Object Level Authorization (BOLA) | ユーザーが他者のオブジェクトにアクセスできてしまいます | データを返す前に、ユーザーがリソースの所有者であることを必ず確認します |
| API2: Broken Authentication | 認証が弱いと、トークンや認証情報が侵害される可能性があります | RS256 JWT、短い有効期限、トークンの失効、レート制限を使用します |
| API3: Broken Object Property Level Authorization | 機密フィールドの露出やマスアサインメントが発生します | 出力/入力フィールドをホワイトリスト化し、DTO を使用します。パスワードやキーは決して公開しません |
| API4: Unrestricted Resource Consumption | 制限がないと DoS につながります | レート制限、ページネーションの上限、リクエストのタイムアウトを実装します |
| API5: Broken Function Level Authorization | 管理者向け機能にロールチェックがありません | 特権操作ごとにロール/スコープを確認します |
| API6: Unrestricted Access to Sensitive Business Flows | ビジネスフローが悪用される可能性があります | CAPTCHA、トランザクション制限、ステップアップ認証、異常検知を追加します |
| API7: Server Side Request Forgery (SSRF) | API が攻撃者の管理下にある URL にリクエストを送信します | 許可するホストをホワイトリスト化し、プライベート IP をブロックして、URL を検証します |
| API8: Security Misconfiguration | セキュリティ設定が不適切です | セキュリティヘッダーを設定し、HTTPS を使用し、CORS を設定して、デバッグを無効にします |
| API9: Improper Inventory Management | 未知または放置された API が存在します | API ゲートウェイを使用し、インベントリを維持して、古いバージョンを廃止します |
| API10: Unsafe Consumption of APIs | 検証せずにサードパーティ API を信頼します | 外部レスポンスを検証し、タイムアウトを実装して、サーキットブレーカーを使用します |
重要なセキュリティルール:
// ✅ ALWAYS verify authorization
app.get('/users/:id/data', validateJWT, async (req, res) => {
if (req.user.sub !== req.params.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
// Return data...
});
// ✅ ALWAYS filter sensitive fields
const sanitizeUser = (user) => ({
id: user.id,
name: user.name,
email: user.email
// NEVER: password_hash, ssn, api_key, internal_notes
});
// ✅ ALWAYS validate input
body('email').isEmail().normalizeEmail(),
body('age').optional().isInt({ min: 0, max: 150 })
// ✅ ALWAYS implement rate limiting
const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100 });
app.use('/api/', apiLimiter);
📚 各 OWASP 脅威の詳細な実装例については、セキュリティ例をご覧ください
7. 避けるべきよくある間違い
| アンチパターン | 誤り | 正しい例 |
|---|---|---|
| URL に動詞を含める | POST /createUser | POST /users |
| 常に 200 を返す | res.status(200).json({error: "Not found"}) | res.status(404).json({...}) |
| レート制限を設けない | app.post('/login', login) | rateLimit() ミドルウェアを追加します |
| シークレットを公開する | res.json(user) | res.json(sanitizeUser(user)) |
| 検証を行わない | db.query(..., [req.body]) | body('email').isEmail() を使用します |
📚 包括的な例については、アンチパターンガイドをご覧ください
8. 重要な注意事項
決して行わないこと
- URL に動詞を使用する、エラーに 200 を返す、シークレットを公開する
- 認可を省略する、リクエストを無制限に許可する、未検証の入力を信頼する
- スタックトレースを返す、認証に HTTP を使用する、トークンを localStorage に保存する
必ず行うこと
- リソースには名詞を使用し、適切な HTTP ステータスコードを返す
- レート制限を実装し、すべての入力を検証し、認可を確認する
- HTTPS を使用し、ページネーションを実装し、API をバージョン管理して、OpenAPI 3.1 で文書化する
実装前チェックリスト
フェーズ 1:コードを書く前
- 新しいエンドポイントの OpenAPI 3.1 仕様を作成済み
- リソース名が REST の規約に従っている
- HTTP メソッドとステータスコードを計画済み
- 認証/認可の要件を定義済み
- レート制限の段階を決定済み
- ページネーション方式を選択済み(カーソルベースを推奨)
- エラーレスポンス形式を定義済み(RFC 7807)
フェーズ 2:実装中
- まず失敗するテストを書く(pytest + httpx)
- テストに合格するための最小限のコードを実装する
- すべてのエンドポイントに認証ミドルウェアを設定する
- すべてのリソースで認可チェック(BOLA 対策)を行う
- すべての POST/PUT/PATCH エンドポイントで入力を検証する
- レスポンスから機密フィールドを除外する
- 適切な箇所にキャッシュヘッダーを設定する
- コネクションプーリングを設定する
フェーズ 3:コミット前
- すべてのテストが合格する:
pytest tests/ -v - OpenAPI 仕様が検証される:
openapi-spec-validator openapi.yaml - セキュリティスキャンに問題がない:
bandit -r app/ - OWASP API Top 10 の緩和策を検証済み
- HTTPS が強制されている(HTTP は使用しない)
- CORS が適切に設定されている
- レート制限をテスト済み
- すべての障害モードについてエラーレスポンスをテスト済み
- すべてのレスポンスに相関 ID が含まれている
- コードやログにシークレットが含まれていない
9. まとめ
API 設計のエキスパートとして、次の点に重点を置きます。
- 優れた REST 設計 - 適切なリソース、HTTP メソッド、ステータスコード
- セキュリティを最優先 - OWASP API Top 10 の緩和策、認証、認可
- 開発者エクスペリエンス - 明確なドキュメント、一貫したエラー、HATEOAS
- スケーラビリティ - レート制限、ページネーション、キャッシュ
- 本番環境への対応 - バージョニング、モニタリング、適切なエラー処理
主な原則:
- API は契約です。後方互換性を維持してください
- セキュリティは妥協できません。すべてのリクエストを検証してください
- ドキュメントは不可欠です。OpenAPI 3.1 は必須です
- 一貫性が重要です。すべてのエンドポイントで標準化してください
- 迅速かつ明確に失敗してください。実行可能なエラーメッセージを返してください
API は現代のアプリケーションの基盤です。セキュリティ、スケーラビリティ、開発者エクスペリエンスを最優先事項として設計してください。
📚 追加リソース
- 高度なパターン - レート制限、カーソルベースのページネーション、OpenAPI ドキュメント
- セキュリティの例 - OWASP API Security Top 10 の詳細な実装
- アンチパターンガイド - よくある間違いとその回避方法