jsonapi
Strict JSON:API v1.1 specification compliance. Trigger: When creating or modifying API endpoints, reviewing API responses, or validating JSON:API compliance.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install prowler-cloud-prowler-jsonapi
Repository
Skill path: skills/jsonapi
Strict JSON:API v1.1 specification compliance. Trigger: When creating or modifying API endpoints, reviewing API responses, or validating JSON:API compliance.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack, Backend.
Target audience: everyone.
License: Apache-2.0.
Original source
Catalog source: SkillHub Club.
Repository owner: prowler-cloud.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install jsonapi into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/prowler-cloud/prowler before adding jsonapi to shared team environments
- Use jsonapi for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: jsonapi
description: >
Strict JSON:API v1.1 specification compliance.
Trigger: When creating or modifying API endpoints, reviewing API responses, or validating JSON:API compliance.
license: Apache-2.0
metadata:
author: prowler-cloud
version: "1.0.0"
scope: [root, api]
auto_invoke:
- "Creating API endpoints"
- "Modifying API responses"
- "Reviewing JSON:API compliance"
---
## 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 |
|-------|-------|
| `jsonapi` | What the spec requires (MUST/MUST NOT rules) |
| `django-drf` | 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 `data` and `errors` in the same response
- ALWAYS include at least one of: `data`, `errors`, `meta`
- ALWAYS use `type` and `id` (string) in resource objects
- NEVER include `id` when creating resources (server generates it)
### Content-Type
- ALWAYS use `Content-Type: application/vnd.api+json`
- ALWAYS use `Accept: application/vnd.api+json`
- NEVER add parameters to media type without `ext`/`profile`
### Resource Objects
- ALWAYS use **string** for `id` (even if UUID)
- ALWAYS use **lowercase kebab-case** for `type`
- NEVER put `id` or `type` inside `attributes`
- NEVER include foreign keys in `attributes` - use `relationships`
### Relationships
- ALWAYS include at least one of: `links`, `data`, or `meta`
- 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 `status` as **string** (e.g., `"400"`, not `400`)
- ALWAYS include `source.pointer` for field-specific errors
---
## HTTP Status Codes (Mandatory)
| Operation | Success | Async | Conflict | Not Found | Forbidden | Bad Request |
|-----------|---------|-------|----------|-----------|-----------|-------------|
| **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` | - |
### When to Use Each
| Code | Use When |
|------|----------|
| `200 OK` | Successful GET, PATCH with response body, DELETE with response |
| `201 Created` | POST created resource (MUST include `Location` header) |
| `202 Accepted` | Async operation started (return task reference) |
| `204 No Content` | Successful DELETE, PATCH with no response body |
| `400 Bad Request` | Invalid query params, malformed request, unknown fields |
| `403 Forbidden` | Authentication ok but no permission, client-generated ID rejected |
| `404 Not Found` | Resource doesn't exist OR RLS hides it (never reveal which) |
| `409 Conflict` | Duplicate ID, type mismatch, relationship conflict |
| `415 Unsupported` | Wrong Content-Type header |
---
## Document Structure
### Success Response (Single)
```json
{
"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)
```json
{
"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
```json
{
"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 |
|--------|--------|---------|
| `page` | `page[number]`, `page[size]` | `?page[number]=2&page[size]=25` |
| `filter` | `filter[field]`, `filter[field__op]` | `?filter[status]=FAIL` |
| `sort` | Comma-separated, `-` for desc | `?sort=-inserted_at,name` |
| `fields` | `fields[type]` | `?fields[providers]=id,alias` |
| `include` | Comma-separated paths | `?include=provider,scan.task` |
### Rules
- MUST return `400` for unsupported query parameters
- MUST return `400` for unsupported `include` paths
- MUST return `400` for unsupported `sort` fields
- MUST NOT include extra fields when `fields[type]` is specified
---
## Common Violations (AVOID)
| Violation | Wrong | Correct |
|-----------|-------|---------|
| ID as integer | `"id": 123` | `"id": "123"` |
| Type as camelCase | `"type": "providerGroup"` | `"type": "provider-groups"` |
| FK in attributes | `"tenant_id": "..."` | `"relationships": {"tenant": {...}}` |
| Errors not array | `{"error": "..."}` | `{"errors": [{"detail": "..."}]}` |
| Status as number | `"status": 400` | `"status": "400"` |
| Data + errors | `{"data": ..., "errors": ...}` | Only one or the other |
| Missing pointer | `{"detail": "Invalid"}` | `{"detail": "...", "source": {"pointer": "..."}}` |
---
## Relationship Updates
### To-One Relationship
```http
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 | `{"data": [{...}, {...}]}` |
| Add members | POST | `{"data": [{...}]}` |
| Remove members | DELETE | `{"data": [{...}]}` |
---
## Compound Documents (`include`)
When using `?include=provider`:
```json
{
"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 `django-drf` skill for DRF-specific patterns
- **Testing**: Use `prowler-test-api` skill for test patterns