rest-api-design について
rest-api-designは、REST/HTTP APIのリソース、動作、契約を設計するスキルです。一貫性と進化しやすさを重視するエンジニア向けに、ステータスコード、エラー、ページネーション、バージョニング、セキュリティを整理し、OpenAPI 3.1の契約に反映します。
主な機能
- 01リソース名詞を軸に一貫したURLを設計
- 02HTTPメソッド、ステータスコード、冪等性を選択
- 03RFC 9457のエラー形式と統一ページネーション
- 04バージョニング、認証、レート制限を策定
- 05設計をOpenAPI 3.1に反映
活用シーン
- 01ドメインエンティティからREST APIを設計
- 02APIのエラー形式やページネーションを統一
- 03リクエスト結果に対応するステータスコードを選択
- 04合意済みのAPI設計をOpenAPI契約に変換
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ bm629/ agent-skills --skill rest-api-designインストール後、Claude Code で/rest-api-designを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/contract-patterns.mderror-model.mdopenapi-mapping.mdsources.md詳細
rest-api-design — SKILL.md
バリアント: standard · 使用するタイミング: スキルが呼び出されると、以下の設計上の判断をリソースの表面に適用し、一貫性のある契約を返します。その後、制御は呼び出し元に戻ります。
概要
このスキルは、REST/HTTP API とその契約を設計するための規律であり、あらゆる Web フレームワークの上位レイヤーにあたります。ドメインの名詞群を、一貫性があり進化させやすい HTTP の表面に落とし込む際に、経験豊富なエンジニアが適用する、明確な既定方針をまとめています。リソースの命名方法、各操作に割り当てるメソッドとステータスコード、機械可読なエラー形式(RFC 9457 problem+json)、構造化された成功レスポンスとページネーション用のエンベロープ、バージョニングの方針、認証とレート制限の適用箇所、そして全体を OpenAPI 3.1 の契約として表現する方法を扱います。コード例は Python/FastAPI と Pydantic v2 を想定していますが、設計上の判断はすべて言語に依存しません。フレームワークの仕組み(ルーター、依存性注入、response_model)とスキーマモデリングは fastapi および pydantic-v2 スキルの対象です。このスキルではそれらを参照するだけで、重複して説明しません。
有効化するタイミング
- ✅ ドメインエンティティ群をもとに、新しい API の表面を設計するとき — エンドポイント、メソッド、契約を決める場合。
- ✅ 結果に適したステータスコードを選ぶとき、または 400 と 422、401 と 403、PUT と PATCH のどちらにするか決めるとき。
- ✅ API のエラーモデル、成功エンベロープ、またはページネーション方式を定義または修正するとき。
- ✅ API のバージョニング、認証、またはレート制限の方針を決めるとき。
- ✅ 合意済みの REST 設計を OpenAPI 3.1 の契約に落とし込むとき。
次の場合は有効化しないでください:
- 設計済みエンドポイントの FastAPI ハンドラー/ルーター/DI コードを書く場合 →
fastapiスキルを使用してください。 - リクエスト/レスポンスのデータクラスを詳細にモデリングする場合 →
pydantic-v2スキルを使用してください。 - 大規模な OpenAPI ドキュメントを作成または生成する場合、リンティングする場合、あるいは SDK のコード生成を実行する場合 → これらは OpenAPI の作成/コード生成に関する専用の作業です(設計から契約への対応付けと引き継ぎ先については、
references/openapi-mapping.mdを参照してください)。 - GraphQL または gRPC の表面を設計する場合 → 対象外です。このスキルが扱うのは REST/HTTP のみです。
ワークフロー
以下の手順を順番に適用してください。各手順には明確な既定方針があります。理由がある場合にのみ、既定方針から外れてください。
ステップ 1:リソースをモデル化する(動詞ではなく名詞)
- リソースは名詞です。動詞は HTTP メソッドで表します。
POST /ordersとし、POST /createOrderにはしません。 - コレクション名は複数形で統一します:
/users、/users/{id}、/users/{id}/orders。/userと/usersを混在させてはいけません。 - ネストは 2 階層までに制限します。
/users/{id}/ordersは問題ありませんが、/users/{id}/orders/{oid}/items/{iid}は避けてください。深い階層の子リソースはトップレベルのリソース(/order-items/{iid})に昇格させ、リンクで関連付けます。 - URL を選ぶ前に、ドメインの名詞とその関係からリソースを特定します。サブリソースは包含/所有関係を表し、横断的な要素は独立したトップレベルのリソースにします。
- URL のパスセグメントは小文字とハイフンで表記し(
/order-items)、camelCaseやsnake_caseは使用しません。 - 行数や列挙可能性が露呈する内部 ID/主キー ID を公開してはいけません。公開契約では UUID または不透明な ID を優先してください。
ステップ 2:メソッドを割り当て、冪等性を守る
RFC 9110 の HTTP セマンティクスに従います。
| メソッド | 用途 | 安全 | 冪等 |
|---|---|---|---|
| GET | リソース/コレクションを読み取る | はい | はい |
| POST | コレクションに作成する、または冪等でないアクションを実行する | いいえ | いいえ |
| PUT | 既知の URL にあるリソースを完全に置き換える | いいえ | はい |
| PATCH | 部分更新する(送信したフィールドのみ) | いいえ | いいえ |
| DELETE | リソースを削除する | いいえ | はい |
- PUT は置き換え、PATCH はマージします。 PUT の本文は新しい完全な表現であり、省略されたフィールドはクリアされます。PATCH の本文には変更するフィールドのみを含めます。部分更新に PUT を使わないでください。
- POST と PATCH は冪等ではありません。POST を再試行すると重複が作成されます。クライアントが再試行する可能性のある作成/課金操作では、
Idempotency-Keyリクエストヘッダー(Stripe の慣例で、現在は IETF のドラフト)を受け付け、キーと最初のレスポンスを保存して、再度リクエストされた際にそれを再送することで、ネットワーク再試行を安全にします。 - GET には副作用があってはなりません。状態を変更する操作は GET ではありません。
ステップ 3:結果に合ったステータスコードを選ぶ
| コード | 意味/使用する場面 |
|---|---|
| 200 OK | GET/PATCH/PUT の成功、または本文を返す DELETE の成功 |
| 201 Created | 作成の成功 — 新しいリソースの URL を Location ヘッダーに設定します |
| 204 No Content | 本文なしで成功(一般的な DELETE、または何も返さない PUT/PATCH) |
| 400 Bad Request | 形式が不正なリクエスト — 解析できない JSON、誤った構造、不正なクエリーエンコーディング |
| 401 Unauthorized | 認証されていない — 認証情報がない、または無効 |
| 403 Forbidden | 認証済みだが、この操作を行う権限がない |
| 404 Not Found | リソースが存在しない(または、この呼び出し元には非表示) |
| 409 Conflict | リクエストが現在の状態と競合している — 一意フィールドの重複、バージョンの不一致 |
| 422 Unprocessable Content | 構文的には有効だが、意味的に無効 — ビジネスルール/フィールドの検証に失敗 |
| 429 Too Many Requests | レート制限を超過 — Retry-After を含めます |
| 5xx | サーバー障害 — 500 は一般的なエラー、503 は利用不可。クライアントの責任にしてはいけません |
- 400 と 422 の違い: 400 はサーバーがリクエストをまったく解析/理解できなかった場合です。422 は解析できたものの、値が検証に失敗した場合です。新しい API では、検証失敗に422、解析失敗に 400をデフォルトとして使用します。
- 401 と 403 の違い: 401 は「あなたは誰ですか」(再認証が必要)、403 は「あなたが誰かはわかっていますが、それでも許可されていません」です。
- エラーを本文に含めた 200 を返してはいけません。 ステータスコードは機械で読み取れる主要なシグナルです。200 で包まれた失敗は、あらゆるクライアント、プロキシ、監視システムを破綻させます。
ステップ 4:エラーモデルを 1 つ定義する — RFC 9457 problem+json(デフォルト)
すべてのエラーレスポンスでは、以下のメンバーを含む application/problem+json(RFC 7807 に取って代わる RFC 9457)を使用します。
type— 問題の種類を識別する URI(安定しており、文書化されているもの。ステータスコード以外に情報がない場合のデフォルトは"about:blank")。title— 種類の簡潔な概要(about:blankの場合は HTTP ステータスの文言、例:"Not Found")。status— HTTP ステータスコード。本文にも重複して記載します。detail— この発生事象についての、人が読める説明。instance— この発生事象を示す URI(例:リクエストパスまたはトレース ID)。- 拡張メンバー — 追加のドメインフィールド。クライアントは、認識できないものを無視 MUST です。フィールド単位の検証エラーは
errors配列に含めます(RFC で認められている拡張です)。
{
"type": "https://api.example.com/problems/validation-error",
"title": "Your request parameters did not validate.",
"status": 422,
"detail": "The request body has 2 invalid fields.",
"instance": "/orders",
"errors": [
{ "detail": "must be a positive integer", "pointer": "#/quantity" },
{ "detail": "must be a valid email", "pointer": "#/customer/email" }
]
}
- API 全体で1 つのモデルを選び、混在させないでください。RFC 9457 は標準であり、機械で解析でき、自己記述的であるため、デフォルトとして適しています。
- 代替案(組織内のスタイルガイドで必須の場合のみ):
{"error": {"code": "...", "message": "...", "details": [...]}}のエンベロープ。機能しますが、標準ではありません。新しい API では problem+json を推奨します。両方を併用しないでください。 - FastAPI での実装:
application/problem+jsonを返す例外ハンドラーを登録し、RequestValidationErrorをerrors配列を含む 422 の problem にマッピングします。references/error-model.mdを参照してください。
ステップ 5:成功レスポンスとページネーションを一つのエンベロープにまとめる
- 単一のリソース:その表現を返します。薄く一貫性のあるラッパー(
{"data": {...}})も、すべての箇所で適用するなら問題ありません。一貫性が、凝った工夫よりも重要です。 - コレクションには必ずページネーションを適用し、上限のないリストは決して返しません。デフォルトの
limitは 20、上限は 100 とし、それを超える値は 422 で拒否します。 - すべてのリストエンドポイントで、一つのエンベロープ形式を使います。
data(項目)、pagination/meta(カーソル/件数)、および任意のlinks(ナビゲーション URL)です。 - 大規模なデータや、頻繁に変化するデータ、フィード形式のデータでは、カーソル(キーベース)方式をデフォルトにします。挿入があっても安定し、深いページでも定数時間で取得でき、コストの高い全件数カウントも不要です。オフセット/ページ方式は、ランダムなページ移動や全件数が必要な、小規模で安定したデータセットにのみ使います。並行書き込みがあるとページずれ(スキップ/重複)が起き、深いページでは大幅に遅くなります。
- どちらのエンベロープ形式も、カーソル方式とオフセット方式の選択基準も、
references/contract-patterns.mdを参照してください。
ステップ 6:バージョニング、認証、レート制限を決める
- バージョニング — URL パスをデフォルトにします:
/v1/users。目に見えてデバッグしやすく、キャッシュにも適しており、複数バージョンを並行稼働できます。URL の分かりやすさよりも発見しやすさを重視する場合は、ヘッダー/メディアタイプによるバージョニングが代替手段になります。 - メジャーバージョンを上げるのは、破壊的変更の場合に限ります(フィールドの削除/改名、型の変更、必須フィールドの追加、同じ状況に対するステータスの変更)。追加的な変更(新しいエンドポイント、新しい任意フィールド)では、バージョンを上げません。クライアントは未知のフィールドを無視する寛容なリーダーとして設計します。
DeprecationヘッダーとSunsetヘッダー(RFC 8594)で廃止を通知し、サンセット後は410 Goneを返します。- 認証は後付けではなく、コントラクトの一部です: ユーザー向け API では
Authorization: Bearer <token>(JWT/OAuth)をデフォルトとし、サービス間通信では API キーヘッダー(例:X-API-Key)を使います。常に HTTPS を使用してください。 OpenAPI では、これらをsecuritySchemesとsecurity要件(操作ごと、またはグローバル)として定義します。 - レート制限:
429+Retry-After(安定した RFC-9110 のデフォルト)を返します。広く導入されているX-RateLimit-Limit/-Remaining/-Resetの規約でクォータを通知します。IETF のRateLimit/RateLimit-Policyフィールドは新たな標準ですが、まだ RFC ではありません。どのクライアントにも理解される安全な基本形は、429+Retry-Afterです。
ステップ 7:設計を OpenAPI 3.1 のコントラクトとして表現する
成果物は、設計をそのまま対応付けられる OpenAPI 3.1 ドキュメントです。リソースは paths、メソッドは操作、エンベロープ/エラー/エンティティの形式は components/schemas、再利用するレスポンス/パラメーターは $ref を介した components/responses + components/parameters、認証は components/securitySchemes に対応します。3.1 を使います(JSON Schema 2020-12 を完全にサポートしており、nullable には旧式の nullable ではなく type: ["string","null"] を使います)。設計ごとに、ひとつの具体的なコントラクトを提示したら、そこで終わりにします。OpenAPI の詳細な記述、リンティング、モックサーバー、SDK コード生成は別の課題です。設計からコントラクトへの完全な対応付けと、完成したコントラクトの例は references/openapi-mapping.md にあります。
ルール
必ず守るルール(例外なし):
- API 全体でエラーモデルは 1 つにします。デフォルトは RFC 9457 の problem+jsonで、
application/problem+jsonとして提供します。2 種類のエラー形式を混在させてはいけません。 - エラーを本文に含めて
200(またはその他の 2xx)を返してはいけません。ステータスコードが契約です。 - リソースは名詞です。パスに動詞を含めてはいけません。コレクションは複数形で統一します。
- すべてのコレクションエンドポイントでページネーションを行い、最大ページサイズを強制します。
- 常に HTTPS を使用します。認証は契約内で宣言し(
securitySchemesとsecurity)、暗黙的にしてはいけません。 - PUT は全置換、PATCH は部分更新です。両者を混同してはいけません。
- 契約には OpenAPI 3.1(JSON Schema 2020-12)を使用します。null 許容性は
"null"型メンバーで表現し、廃止された 3.0 のnullableキーワードは使いません。
推奨事項(変更可能):
- デフォルトではカーソル/キーセットページネーションを使います。オフセットは、小規模で安定しており、ランダムアクセスが必要なデータセットに限ります。
- デフォルトでは URL パスでバージョンを管理します。メジャーバージョンのみを使い、追加的な変更ではバージョンを上げません。
- ユーザー向け API には Bearer/JWT、サービス間通信には API キーを使います。
- レート制限の基本は
429とRetry-Afterにします。補足情報としてX-RateLimit-*ヘッダーを追加します。 - リソースのネストは 2 階層までに制限し、それより深い子リソースはトップレベルのリソースにします。
注意点
- **PATCH を全置換として扱うと、**クライアントが送信しなかったフィールドが気付かないうちに消去されます。PATCH では指定されたフィールドのみをマージする必要があります。置換の動作が必要なら PUT を使います。
- **エラーを本文に含めた 200 は、**ブラウザーでは問題ないように見えても、ステータスコードを基準に動作する再試行ロジック、キャッシュ、監視を壊します。常に実際のステータスを設定してください。
- **オフセットページネーションのずれ:**並行して挿入や削除が行われると、ページ間で
?offset=により行がスキップされたり重複したりします。また、深いオフセットほど処理が遅くなります(データベースがスキップ対象の行をすべてスキャンして破棄するためです)。カーソル/キーセット方式なら、どちらも回避できます。 - **422 と 400 の混同:**構文解析には成功したものの、業務上の検証に失敗した値に 400 を返すと、クライアントが処理を分岐するために使う情報が失われます。400 は「まったく解析できなかった」場合に限って使います。
- **OpenAPI 3.1 のスキーマで
nullable: trueを指定しても、**3.0 のキーワードであるため、気付かないうちに無視されます。type: ["string", "null"]を使ってください。バリデーターが常に警告するとは限りません。 - **POST のべき等性を前提にしてはいけません。**タイムアウトした POST をクライアント(およびプロキシ)が再試行すると、重複が作成される場合があります。再試行が想定される場合は、
Idempotency-Keyを必須にし、サーバー側で重複排除を行います。 - 連番の内部 ID を漏えいすると(
/users/1001)、行数が露呈し、列挙も可能になります。公開 API では不透明な ID/UUID を使います。
アンチパターン
- 「とりあえず 200 を返して、本文に
ok: falseを入れればいい」— いけません。ステータスコードが機械可読な契約です。 - 「動詞のほうが分かりやすいから、
/getUserで問題ない」— いけません。動詞はメソッドで、パスは名詞です。 - 「サブシステムごとにエラー形式を分ける」— 形式は 1 つ(problem+json)に決め、全体に適用します。
- 「このコレクションは小さいからページネーションは不要」— コレクションは増えます。上限のないリストは、将来的な障害の火種です。
- 「エンドポイントごとにバージョンを付ける/URL にマイナーバージョンを使う」— API 全体をメジャーバージョンのみでバージョン管理します。
- 「ここで FastAPI のルーティングや Pydantic のモデリングを一から説明する」—
fastapiとpydantic-v2のスキルを参照してください。このスキルが扱うのは設計であり、フレームワークのコードではありません。
出力
このスキルでは、REST API の設計と OpenAPI 3.1 の契約を作成します。内容には、リソース/URL のマップ、各操作のメソッドとステータスコードの判断、単一のエラーモデル(problem+json)と成功時/ページネーション用のエンベロープ、バージョン管理/認証/レート制限の方針、およびそれらを表現した OpenAPI 3.1 ドキュメントの例が含まれます。想定される利用者は、fastapi と pydantic-v2 のスキルを使ってハンドラーを実装するエンジニア(またはコーディングエージェント)と、公開された契約に基づいて連携する下流のクライアントです。
関連項目
fastapi— フレームワークの仕組み(ルーター、依存性注入、response_model、例外ハンドラー)。このスキルが契約を決定し、fastapiが実装します。pydantic-v2— リクエスト/レスポンスのスキーマモデリング。このスキルがデータ構造を定義し、pydantic-v2がモデル化します。- OpenAPI 作成/SDK コード生成ツール — より詳細な契約の作成、リンティング、モックサーバー、クライアント生成(このスキルから引き継ぐ別の領域です)。
references/error-model.md、references/contract-patterns.md、references/openapi-mapping.md— 以下の読み込み条件を参照してください。
段階的な開示
詳細な内容は references/ に格納されており、必要な場合にのみ読み込みます。
references/error-model.md— エラーモデルを定義または修正するときに読み込みます。RFC 9457 の各メンバー、errors検証配列、about:blankのデフォルト、404 と 422 の problem+json の例、FastAPI の例外ハンドラーパターン、比較用の標準外の{error:{...}}形式を扱います。references/contract-patterns.md— レスポンスやページネーションを設計するときに読み込みます。成功時のエンベロープ、レスポンス形式を含むカーソル方式とオフセット方式の決定表、バージョン管理のライフサイクル(Deprecation/Sunset/410)、認証方式、レート制限ヘッダーを扱います。references/openapi-mapping.md— 設計を契約として表現するときに読み込みます。設計から OpenAPI 3.1 要素へのマッピング、3.1 と 3.0 の違いに関する注記(JSON Schema 2020-12、null 型、Webhook)、problem+json レスポンスとセキュリティスキームを含む、全工程を網羅した契約例を扱います。references/sources.md— 調査の出典/引用情報。
本文の上限
descriptionは 1,024 文字以下(agentskills.io の上限)。- 本文は約 500 行/5,000 トークン以下にし、毎回のコンテキストに含めます。
- 参照ファイルごとに、10k トークン超で警告、25k トークン超でエラー。参照ファイル全体では、25k トークン超で警告、50k トークン超でエラー。