Resources reference each other: a book has an author, an author has many books. Plain REST gives you two bad defaults: embed everything (bloated responses) or return only IDs and force the client into follow-up requests. That second one is the network N+1 problem :
GET /books → [{ id: 1, authorId: "a1" }, { id: 2, authorId: "a2" }, ...]
GET /authors/a1 → { name: "Fitzgerald" }
GET /authors/a2 → { name: "Orwell" }
...one more round trip per author JSON:API handles this in two parts. First, every resource declares its relationships as type/id pairs plus links to fetch them:
{
"type": "books",
"id": "42",
"attributes": { "title": "The Hobbit" },
"relationships": {
"author": {
"data": { "type": "authors", "id": "7" },
"links": { "related": "/books/42/author" }
}
}
}
Second, the client can ask for related resources in the same response with
include. The result is a compound document : related records arrive in a
top-level included array, each one appearing exactly once no matter how many books
reference it.
{
"data": [
{
"type": "books", "id": "42",
"attributes": { "title": "The Hobbit" },
"relationships": { "author": { "data": { "type": "authors", "id": "7" } } }
}
],
"included": [
{ "type": "authors", "id": "7", "attributes": { "name": "J.R.R. Tolkien" } }
]
}
One round trip, no duplicated author records, and the client stitches records together by
type + id.
Check your understanding
The cost of returning only IDs
A /books response returns each book with an authorId but no author details. The client needs every book's author name to render the list. What happens, and what does include fix?