Stop bikeshedding your response shape
Every team that builds a REST API re-invents the same decisions: where does the data go, how are errors shaped, how do you include related resources? JSON:API is a specification that answers all of this once, so your team (and your clients) stop arguing about envelopes and start building. Adopt it and a lot of design debate simply disappears.
The document structure
A JSON:API response wraps data in a consistent envelope:
{
"data": {
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON:API explained",
"published": true
},
"relationships": {
"author": { "data": { "type": "people", "id": "9" } }
}
}
}Every resource has a `type`, an `id`, its `attributes`, and optional `relationships`. Collections put an array in data. This uniformity means a generic client can consume any JSON:API endpoint.
Relationships and compound documents
The standout feature is `included` — return a resource and its related resources in one response, avoiding extra round-trips:
{
"data": { "type": "articles", "id": "1",
"relationships": { "author": { "data": { "type": "people", "id": "9" } } } },
"included": [
{ "type": "people", "id": "9", "attributes": { "name": "Ada" } }
]
}The client asks for what it wants with ?include=author, and the server returns a compound document. It is the over-fetching/under-fetching solution that pushed many teams toward GraphQL — available within plain REST.
Sparse fieldsets, sorting, pagination
JSON:API standardizes the query conventions too:
GET /articles?fields[articles]=title&sort=-published&page[size]=10fields[type]— return only the attributes you need (sparse fieldsets).sort— comma-separated fields;-for descending.page[...]— standardized pagination params.
Because everyone follows the same rules, tooling and clients work across APIs.
A standard error shape
Errors are an array of structured objects, so clients can handle them uniformly:
{
"errors": [
{
"status": "422",
"source": { "pointer": "/data/attributes/title" },
"title": "Invalid Attribute",
"detail": "Title must be at least 3 characters."
}
]
}The source.pointer tells a form exactly which field failed — no custom parsing.
Is it worth it?
| JSON:API fits when… | Skip it when… |
|---|---|
| Multiple clients consume the API | A single tightly-coupled frontend |
| You have many related resources | A handful of simple endpoints |
| You want off-the-shelf tooling | You need a very compact, custom shape |
The format is more verbose than a hand-rolled envelope, which is the main trade-off. For public or multi-client APIs the consistency usually wins; for a small internal API it can be overkill.