jsonapi について
jsonapiは、JSON:APIエンドポイントの実装やレビュー向けのコーディングスキルです。ドキュメント構造、リソース、関係、エラー、クエリパラメーター、HTTPステータスコードの仕様ルールをまとめています。
主な機能
- 01レスポンスとリソース構造のルール
- 02Content-Typeと識別子の要件
- 03関係とincludeのガイダンス
- 04エラー形式とステータスコード
- 05クエリパラメーター検証ルール
活用シーン
- 01エンドポイントのJSON:API準拠確認
- 02関係更新リクエストの設計
- 03APIエラーとクエリ処理のレビュー
MCP 対応
Context7 MCPを使って、最新のJSON:API仕様を確認する方法を推奨しています。
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ prowler-cloud/ prowler --skill jsonapiインストール後、Claude Code で/jsonapiを入力して読み込まれたか確認します。
詳細
django-drf での使用
このスキルは仕様への準拠に重点を置いています。実装パターン(ViewSet、Serializer、Filter)については、このスキルとあわせて django-drf スキルを使用してください。
| スキル | 対象 |
|---|---|
jsonapi | 仕様で要求される事項(MUST/MUST NOT のルール) |
django-drf | DRF での実装方法(コードパターン) |
エンドポイントを作成または変更する際は、両方のスキルを呼び出してください。
実装/レビューの前に
エンドポイントを作成または変更する前に、必ず最新の仕様に照らして検証してください。
オプション 1:Context7 MCP(推奨)
Context7 MCP が利用可能な場合は、JSON:API 仕様を直接照会してください。
mcp_context7_resolve-library-id(query="jsonapi specification")
mcp_context7_query-docs(libraryId="<resolved-id>", query="[specific topic: relationships, errors, etc.]")
オプション 2:WebFetch(代替手段)
Context7 が利用できない場合は、公式仕様を取得してください。
WebFetch(url="https://jsonapi.org/format/", prompt="Extract rules for [specific topic]")
これにより、仕様が更新された後も、最新の JSON:API バージョンに準拠できます。
重要なルール(絶対に破らないでください)
ドキュメント構造
- 同じレスポンスに
dataとerrorsを両方含めてはいけません data、errors、metaのうち、少なくとも 1 つを必ず含めてください- リソースオブジェクトでは、必ず
typeとid(文字列)を使用してください - リソースを作成する際に
idを含めてはいけません(サーバーが生成します)
Content-Type
- 必ず
Content-Type: application/vnd.api+jsonを使用してください - 必ず
Accept: application/vnd.api+jsonを使用してください ext/profileがない場合、メディアタイプにパラメーターを追加してはいけません
リソースオブジェクト
idには必ず文字列を使用してください(UUID の場合も同様です)typeには必ず小文字のケバブケースを使用してくださいattributesの中にidやtypeを入れてはいけません- 外部キーを
attributesに含めてはいけません。relationshipsを使用してください
リレーションシップ
links、data、metaのうち、少なくとも 1 つを必ず含めてください- 必ずリソースリンク形式
{"type": "...", "id": "..."}を使用してください - リレーションシップに生の ID を使用してはいけません。必ずリンクオブジェクトを使用してください
エラーオブジェクト
- エラーは必ず配列として返してください:
{"errors": [...]} statusは必ず文字列で含めてください(例:"400"。400ではありません)- フィールド固有のエラーには、必ず
source.pointerを含めてください
HTTP ステータスコード(必須)
| 操作 | 成功 | 非同期 | 競合 | 見つからない | 禁止 | 不正なリクエスト |
|---|---|---|---|---|---|---|
| GET | 200 | - | - | 404 | 403 | 400 |
| POST | 201 | 202 | 409 | 404 | 403 | 400 |
| PATCH | 200 | 202 | 409 | 404 | 403 | 400 |
| DELETE | 200/204 | 202 | - | 404 | 403 | - |
使い分け
| コード | 使用する場合 |
|---|---|
200 OK | GET の成功、レスポンスボディを返す PATCH、レスポンスを返す DELETE |
201 Created | POST でリソースを作成した場合(Location ヘッダーを必ず含めてください) |
202 Accepted | 非同期操作を開始した場合(タスク参照を返してください) |
204 No Content | DELETE の成功、またはレスポンスボディを返さない PATCH |
400 Bad Request | クエリパラメーターが無効、不正なリクエスト、不明なフィールド |
403 Forbidden | 認証は成功したものの権限がない場合、クライアント生成 ID が拒否された場合 |
404 Not Found | リソースが存在しない、または RLS により非表示の場合(どちらであるかを決して明かさないでください) |
409 Conflict | ID の重複、type の不一致、リレーションシップの競合 |
415 Unsupported | Content-Type ヘッダーが誤っている場合 |
ドキュメント構造
成功レスポンス(単一)
{
"data": {
"type": "providers",
"id": "550e8400-e29b-41d4-a716-446655440000",
"attributes": {
"alias": "Production",
"connected": true
},
"relationships": {
"tenant": {
"data": {"type": "tenants", "id": "..."}
}
},
"links": {
"self": "/api/v1/providers/550e8400-..."
}
},
"links": {
"self": "/api/v1/providers/550e8400-..."
}
}
成功レスポンス(リスト)
{
"data": [
{"type": "providers", "id": "...", "attributes": {...}},
{"type": "providers", "id": "...", "attributes": {...}}
],
"links": {
"self": "/api/v1/providers?page[number]=1",
"first": "/api/v1/providers?page[number]=1",
"last": "/api/v1/providers?page[number]=5",
"prev": null,
"next": "/api/v1/providers?page[number]=2"
},
"meta": {
"pagination": {"count": 100, "pages": 5}
}
}
エラーレスポンス
{
"errors": [
{
"status": "400",
"code": "invalid",
"title": "Invalid attribute",
"detail": "UID must be 12 digits for AWS accounts",
"source": {"pointer": "/data/attributes/uid"}
}
]
}
クエリパラメーター
| ファミリー | 形式 | 例 |
|---|---|---|
page | page[number]、page[size] | ?page[number]=2&page[size]=25 |
filter | filter[field]、filter[field__op] | ?filter[status]=FAIL |
sort | カンマ区切り、降順には - | ?sort=-inserted_at,name |
fields | fields[type] | ?fields[providers]=id,alias |
include | カンマ区切りのパス | ?include=provider,scan.task |
ルール
- サポートされていないクエリパラメーターには、
400を必ず返してください - サポートされていない
includeパスには、400を必ず返してください - サポートされていない
sortフィールドには、400を必ず返してください fields[type]が指定されている場合、追加のフィールドを含めてはいけません
よくある違反(避けてください)
| 違反 | 誤り | 正しい例 |
|---|---|---|
| ID が整数 | "id": 123 | "id": "123" |
| type が camelCase | "type": "providerGroup" | "type": "provider-groups" |
| 外部キーが attributes 内にある | "tenant_id": "..." | "relationships": {"tenant": {...}} |
| エラーが配列ではない | {"error": "..."} | {"errors": [{"detail": "..."}]} |
| status が数値 | "status": 400 | "status": "400" |
| data と errors の併記 | {"data": ..., "errors": ...} | いずれか一方のみ |
| pointer がない | {"detail": "Invalid"} | {"detail": "...", "source": {"pointer": "..."}} |
リレーションシップの更新
単一リレーションシップ
PATCH /api/v1/providers/123/relationships/tenant
Content-Type: application/vnd.api+json
{"data": {"type": "tenants", "id": "456"}}
クリアする場合:{"data": null}
複数リレーションシップ
| 操作 | メソッド | 本文 |
|---|---|---|
| すべて置き換える | PATCH | {"data": [{...}, {...}]} |
| メンバーを追加する | POST | {"data": [{...}]} |
| メンバーを削除する | DELETE | {"data": [{...}]} |
複合ドキュメント(include)
?include=provider を使用する場合:
{
"data": {
"type": "scans",
"id": "...",
"relationships": {
"provider": {
"data": {"type": "providers", "id": "prov-123"}
}
}
},
"included": [
{
"type": "providers",
"id": "prov-123",
"attributes": {"alias": "Production"}
}
]
}
ルール
- 含めるすべてのリソースは、プライマリデータからリレーションシップの連鎖をたどって到達できなければなりません
- 孤立したリソースを含めてはなりません
- リソースを重複して含めてはなりません(同じ type+id)
仕様の参照先
- 仕様全文:https://jsonapi.org/format/
- 実装:DRF 固有のパターンには
django-drfスキルを使用します - テスト:テストパターンには
prowler-test-apiスキルを使用します
コメント
Markdown 対応