Reducing included fields in JSON:API responsesfor Drupal 11 , 10 , 9 , and 8

Last updated :  

A single naive JSON:API request for "an article and everything related to it" can trigger a cascade — the article, its author, its tags, each tag's related articles, each related article's own author — turning one HTTP request into a query load resembling dozens of them server-side. This lesson is about controlling that cascade deliberately, instead of letting a client's query shape dictate your server's workload.

What you'll learn in this lesson

  • How the include parameter can silently multiply query cost
  • Filtering and pagination as query-shaping tools, not just convenience features
  • Why JSON:API's own resource-type configuration lets you restrict what's exposed at all

The include cascade

# Innocent-looking, expensive in practice:
GET /jsonapi/node/article/{uuid}?include=uid,field_tags,field_tags.field_related_articles
Each level of include is a real, additional set of entity loads. include=uid loads the author. Adding field_tags loads every tag. Adding field_tags.field_related_articles loads every related article for every one of those tags — a client requesting a deeply nested include chain can generate load disproportionate to what looks like "one API request" in their own client code. This is architecturally the same N+1-adjacent concern from the Entity and Field Optimization section, just triggered by a client's query shape instead of your own code's loop.

Filtering and pagination as real cost controls

# Without a limit, a collection endpoint returns everything matching —
# potentially thousands of full entity representations in one response.
GET /jsonapi/node/article?filter[status]=1&page[limit]=20&page[offset]=0

page[limit] isn't just a client convenience for pagination UI — it's a genuine bound on how much your server has to serialize and transmit per request. JSON:API defaults to a reasonable limit already, but confirm your own jsonapi.settings configuration hasn't been changed to something unbounded.

Restricting what's exposed at the resource-type level

# /admin/config/services/jsonapi — per-resource-type field restrictions
# and whether a resource type is even exposed at all.

JSON:API's own admin configuration lets you disable specific resource types entirely, or restrict which fields are ever exposed through it — a structural limit that applies regardless of what any individual client requests, useful when certain content or fields genuinely shouldn't be queryable via the API at all, for either performance or security reasons.

Quick check: a client's JSON:API request uses a 3-level-deep include chain and no page[limit], and your server starts timing out. Which single change addresses the immediate problem fastest? If you said adding a page[limit] bound (client-side, or by lowering the server default), you've correctly identified that unbounded collection size compounds with deep includes multiplicatively — fixing either helps, but bounding result count usually has the bigger immediate effect.

Key takeaways

  • Each level of a JSON:API include chain triggers real, additional entity loads — depth matters, not just breadth.
  • page[limit] is a genuine cost control, not just a UI convenience — confirm your server-side default is actually bounded.
  • JSON:API's own admin configuration can restrict exposed resource types and fields structurally, independent of any client request.
  • A deep include chain combined with an unbounded collection compounds cost multiplicatively — address both, not just one.

Coming up next

You've controlled how much gets fetched and included. Next, we control how much of each individual resource actually gets returned, using sparse fieldsets.