Claude-skill-registry jsonapi
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/jsonapi" ~/.claude/skills/majiayu000-claude-skill-registry-jsonapi && rm -rf "$T"
manifest:
skills/data/jsonapi/SKILL.mdsource content
Use With django-drf
This skill focuses on spec compliance. For implementation patterns (ViewSets, Serializers, Filters), use
django-drf skill together with this one.
| Skill | Focus |
|---|---|
| What the spec requires (MUST/MUST NOT rules) |
| How to implement it in DRF (code patterns) |
When creating/modifying endpoints, invoke BOTH skills.
Before Implementing/Reviewing
ALWAYS validate against the latest spec before creating or modifying endpoints:
Option 1: Context7 MCP (Preferred)
If Context7 MCP is available, query the JSON:API spec directly:
mcp_context7_resolve-library-id(query="jsonapi specification") mcp_context7_query-docs(libraryId="<resolved-id>", query="[specific topic: relationships, errors, etc.]")
Option 2: WebFetch (Fallback)
If Context7 is not available, fetch from the official spec:
WebFetch(url="https://jsonapi.org/format/", prompt="Extract rules for [specific topic]")
This ensures compliance with the latest JSON:API version, even after spec updates.
Critical Rules (NEVER Break)
Document Structure
- NEVER include both
anddata
in the same responseerrors - ALWAYS include at least one of:
,data
,errorsmeta - ALWAYS use
andtype
(string) in resource objectsid - NEVER include
when creating resources (server generates it)id
Content-Type
- ALWAYS use
Content-Type: application/vnd.api+json - ALWAYS use
Accept: application/vnd.api+json - NEVER add parameters to media type without
/extprofile
Resource Objects
- ALWAYS use string for
(even if UUID)id - ALWAYS use lowercase kebab-case for
type - NEVER put
orid
insidetypeattributes - NEVER include foreign keys in
- useattributesrelationships
Relationships
- ALWAYS include at least one of:
,links
, ordatameta - ALWAYS use resource linkage format:
{"type": "...", "id": "..."} - NEVER use raw IDs in relationships - always use linkage objects
Error Objects
- ALWAYS return errors as array:
{"errors": [...]} - ALWAYS include
as string (e.g.,status
, not"400"
)400 - ALWAYS include
for field-specific errorssource.pointer
HTTP Status Codes (Mandatory)
| Operation | Success | Async | Conflict | Not Found | Forbidden | Bad Request |
|---|---|---|---|---|---|---|
| GET | | - | - | | | |
| POST | | | | | | |
| PATCH | | | | | | |
| DELETE | / | | - | | | - |
When to Use Each
| Code | Use When |
|---|---|
| Successful GET, PATCH with response body, DELETE with response |
| POST created resource (MUST include header) |
| Async operation started (return task reference) |
| Successful DELETE, PATCH with no response body |
| Invalid query params, malformed request, unknown fields |
| Authentication ok but no permission, client-generated ID rejected |
| Resource doesn't exist OR RLS hides it (never reveal which) |
| Duplicate ID, type mismatch, relationship conflict |
| Wrong Content-Type header |
Document Structure
Success Response (Single)
{ "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-..." } }
Success Response (List)
{ "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} } }
Error Response
{ "errors": [ { "status": "400", "code": "invalid", "title": "Invalid attribute", "detail": "UID must be 12 digits for AWS accounts", "source": {"pointer": "/data/attributes/uid"} } ] }
Query Parameters
| Family | Format | Example |
|---|---|---|
| , | |
| , | |
| Comma-separated, for desc | |
| | |
| Comma-separated paths | |
Rules
- MUST return
for unsupported query parameters400 - MUST return
for unsupported400
pathsinclude - MUST return
for unsupported400
fieldssort - MUST NOT include extra fields when
is specifiedfields[type]
Common Violations (AVOID)
| Violation | Wrong | Correct |
|---|---|---|
| ID as integer | | |
| Type as camelCase | | |
| FK in attributes | | |
| Errors not array | | |
| Status as number | | |
| Data + errors | | Only one or the other |
| Missing pointer | | |
Relationship Updates
To-One Relationship
PATCH /api/v1/providers/123/relationships/tenant Content-Type: application/vnd.api+json {"data": {"type": "tenants", "id": "456"}}
To clear:
{"data": null}
To-Many Relationship
| Operation | Method | Body |
|---|---|---|
| Replace all | PATCH | |
| Add members | POST | |
| Remove members | DELETE | |
Compound Documents (include
)
includeWhen using
?include=provider:
{ "data": { "type": "scans", "id": "...", "relationships": { "provider": { "data": {"type": "providers", "id": "prov-123"} } } }, "included": [ { "type": "providers", "id": "prov-123", "attributes": {"alias": "Production"} } ] }
Rules
- Every included resource MUST be reachable via relationship chain from primary data
- MUST NOT include orphan resources
- MUST NOT duplicate resources (same type+id)
Spec Reference
- Full Specification: https://jsonapi.org/format/
- Implementation: Use
skill for DRF-specific patternsdjango-drf - Testing: Use
skill for test patternsprowler-test-api