jsonapi
Prowler is the world’s most widely used open-source cloud security platform that automates security and compliance across any cloud environment.
12.8K stars1.9K forksUpdated Jan 26, 2026
npx skills add https://github.com/prowler-cloud/prowler --skill jsonapiSKILL.md
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
dataanderrorsin the same response - ALWAYS include at least one of:
data,errors,meta - ALWAYS use
typeandid(string) in resource objects - NEVER include
idwhen 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
idortypeinsideattributes - NEVER include foreign keys in
attributes- userelationships
Relationships
- ALWAYS include at least one of:
links,data, ormeta - 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
statusas string (e.g.,"400", not400) - ALWAYS include
source.pointerfor 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)
{
"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 |
|---|---|---|
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-sepa |
...
Repository
prowler-cloud/prowlerParent repository
Repository Stats
Stars12.8K
Forks1.9K
LicenseApache License 2.0