Django's Rest Framework's JSON:API has different capabilities. Add or modify Viking to enable easier extension for different apis
Routes
| Operation |
Match |
Summary |
Viking |
JSON:API |
| List |
✅ |
|
GET /models |
GET /models/ |
| Calculate |
❌ |
unsupported |
supported |
no support |
| Detail |
✅ |
|
GET /models/{id} |
GET /models/{id}/ |
| Create |
✅ |
|
POST /models |
POST /models/ |
| Update |
⚠️ |
PUT vs PATCH |
PUT /models/{id} |
PATCH /models/{id}/ |
| Delete |
✅ |
|
DELETE /models/{id} |
DELETE /models/{id}/ |
| Resource Names |
⚠️ |
stringify difference |
/my_users |
/my-users |
Nested / relationship routes
| Operation |
Match |
Summary |
Viking |
JSON:API |
| Create association |
❌ |
unsupported |
POST /owners/{id}/children |
unsupported |
| Add association |
⚠️ |
body shape difference and route difference |
POST /owners/{id}/children/{child_id} |
POST /articles/{id}/relationships/children |
| Remove association |
⚠️ |
body shape difference and route difference |
DELETE /owners/{id}/children/{child_id} |
DELETE /articles/{id}/relationships/children |
DRF route and body shape
POST /api/users/abc-123/relationships/teams
{
"data": [
{ "type": "teams", "id": "t3" }
]
}
Query parameters
| Operation |
Match |
Viking |
JSON:API |
| Filtering |
⚠️ |
?where[field]=value |
?filter[field]=value |
| Sorting |
⚠️ |
?order[asc]=field |
?sort=-field |
| Pagination |
⚠️ |
?limit=10&offset=0 |
?page[number]=1&page[size]=10 |
| Includes |
⚠️ |
?include[]=author&include[]=comments |
?include=author,comments |
- Need furter investigation into query string parsing, may need DRF adapter for viking style
where[]= and where[user][name][gt]=
Request Bodies
Create (POST)
| Operation |
Match |
Summary |
Viking |
JSON:API |
| Envelope |
⚠️ |
resource_name vs data wrapper |
{ "user": { attrs } } |
{ "data": { "type", "attributes", "relationships" } } |
| Attributes |
✅ |
Flat key-value |
{ "name": "Ben" } |
{ "attributes": { "name": "Ben" } } |
| Relationships |
⚠️ |
Inline nested vs resource identifiers |
{ "team": { "id": 5, "name": "Alpha" } } |
{ "relationships": { "team": { "data": { "type": "teams", "id": "5" } } } } |
Update (PUT/PATCH)
| Operation |
Match |
Summary |
Viking |
JSON:API |
| Envelope |
⚠️ |
resource_name vs data wrapper |
{ "model": { "name": "Updated" } } |
{ "data": { "type", "id", "attributes": { "name": "Updated" } } } |
| Partial update |
✅ |
Dirty tracking / PATCH semantics |
Changed attrs only |
Changed attrs only |
| Type + ID in body |
⚠️ |
Not required vs required |
Not included |
Must include type and id |
Associations in requests
| Operation |
Match |
Summary |
Viking |
JSON:API |
| Inline nested create/update |
❌ |
Full object vs {type, id} only |
{ "phase": { "id": 11, "name": "Jerry" } } |
{ "phase": { "data": { "type": "phases", "id": "11" } } } |
Verdict: Fundamental difference. Viking sends full nested attribute data for associations (autosave). JSON:API only sends {type, id} linkages — you cannot create/update related records inline. Related records must be created via their own endpoints first.
Response Bodies
Single record
| Operation |
Match |
Summary |
Viking |
JSON:API |
| Envelope |
⚠️ |
Flat object vs data wrapper |
{ "id": 1, "name": "Ben", ... } |
{ "data": { "type", "id", "attributes", "relationships" } } |
| Attributes |
⚠️ |
Flat vs nested under attributes |
{ "name": "Ben" } |
{ "attributes": { "name": "Ben" } } |
| Related records |
⚠️ |
Inline nesting vs sideloaded included |
{ "team": { "id": 5, "name": "Alpha" } } |
"included": [{ "type": "teams", "id": "5", "attributes": { "name": "Alpha" } }] |
Collection
| Operation |
Match |
Summary |
Viking |
JSON:API |
| Envelope |
⚠️ |
Raw array vs data wrapper |
[{ "id": 1 }, { "id": 2 }] |
{ "data": [...], "links": {}, "meta": {} } |
| Related records |
⚠️ |
Inline nesting vs sideloaded included |
[{ "id": 5, "teams": [{"id": 21}]}, { "id": 5, "teams": [{"id": 27}]}] |
"data": [{ "id": 5, "relationships": {"teams": [{"id": 21}]}}, { "id": 5, "relationships": {"teams": [{"id": 27}]}}] "included": [{"id": 21}, {"id": 27}] |
Delete
| Operation |
Match |
Summary |
Viking |
JSON:API |
| Response |
✅ |
HTTP 204, no body |
204 null |
204 no body |
Errors
| Operation |
Match |
Summary |
Viking |
JSON:API |
| Envelope |
⚠️ |
Both use top-level errors key |
{ "errors": { ... } } |
{ "errors": [ ... ] } |
| Structure |
❌ |
Flat object vs array of error objects |
{ "name": "can't be blank" } |
[{ "status": "422", "source": { "pointer": "/data/attributes/name" }, "detail": "..." }] |
Headers
| Operation |
Match |
Summary |
Viking |
JSON:API |
| Accept |
⚠️ |
application/json vs vnd.api+json |
application/json |
application/vnd.api+json |
| Content-Type |
⚠️ |
application/json vs vnd.api+json |
application/json |
application/vnd.api+json |
| CSRF Token |
⚠️ |
Meta tag vs cookie |
X-CSRF-Token from meta tag |
Typically via cookie (DRF default) |
Verdict: Headers differ. JSON:API requires the application/vnd.api+json media type. The new csrfToken() hook and constructor headers option can handle this.
Django's Rest Framework's JSON:API has different capabilities. Add or modify Viking to enable easier extension for different apis
Routes
GET /modelsGET /models/GET /models/{id}GET /models/{id}/POST /modelsPOST /models/PUTvsPATCHPUT /models/{id}PATCH /models/{id}/DELETE /models/{id}DELETE /models/{id}//my_users/my-usersNested / relationship routes
POST /owners/{id}/childrenPOST /owners/{id}/children/{child_id}POST /articles/{id}/relationships/childrenDELETE /owners/{id}/children/{child_id}DELETE /articles/{id}/relationships/childrenDRF route and body shape
POST /api/users/abc-123/relationships/teamsQuery parameters
?where[field]=value?filter[field]=value?order[asc]=field?sort=-field?limit=10&offset=0?page[number]=1&page[size]=10?include[]=author&include[]=comments?include=author,commentswhere[]=andwhere[user][name][gt]=Request Bodies
Create (POST)
resource_namevsdatawrapper{ "user": { attrs } }{ "data": { "type", "attributes", "relationships" } }{ "name": "Ben" }{ "attributes": { "name": "Ben" } }{ "team": { "id": 5, "name": "Alpha" } }{ "relationships": { "team": { "data": { "type": "teams", "id": "5" } } } }Update (PUT/PATCH)
resource_namevsdatawrapper{ "model": { "name": "Updated" } }{ "data": { "type", "id", "attributes": { "name": "Updated" } } }typeandidAssociations in requests
{type, id}only{ "phase": { "id": 11, "name": "Jerry" } }{ "phase": { "data": { "type": "phases", "id": "11" } } }Verdict: Fundamental difference. Viking sends full nested attribute data for associations (autosave). JSON:API only sends
{type, id}linkages — you cannot create/update related records inline. Related records must be created via their own endpoints first.Response Bodies
Single record
datawrapper{ "id": 1, "name": "Ben", ... }{ "data": { "type", "id", "attributes", "relationships" } }attributes{ "name": "Ben" }{ "attributes": { "name": "Ben" } }included{ "team": { "id": 5, "name": "Alpha" } }"included": [{ "type": "teams", "id": "5", "attributes": { "name": "Alpha" } }]Collection
datawrapper[{ "id": 1 }, { "id": 2 }]{ "data": [...], "links": {}, "meta": {} }included[{ "id": 5, "teams": [{"id": 21}]}, { "id": 5, "teams": [{"id": 27}]}]"data": [{ "id": 5, "relationships": {"teams": [{"id": 21}]}}, { "id": 5, "relationships": {"teams": [{"id": 27}]}}] "included": [{"id": 21}, {"id": 27}]Delete
204 null204 no bodyErrors
errorskey{ "errors": { ... } }{ "errors": [ ... ] }{ "name": "can't be blank" }[{ "status": "422", "source": { "pointer": "/data/attributes/name" }, "detail": "..." }]Headers
application/jsonvsvnd.api+jsonapplication/jsonapplication/vnd.api+jsonapplication/jsonvsvnd.api+jsonapplication/jsonapplication/vnd.api+jsonX-CSRF-Tokenfrom meta tagVerdict: Headers differ. JSON:API requires the
application/vnd.api+jsonmedia type. The newcsrfToken()hook and constructorheadersoption can handle this.