skip to content

How do you set page-size caps across nested GraphQL connections when each level multiplies the leaf count?

level: principalimportance: should knowfreq 31%

answer

  1. Multiply the path, do not add
  2. Defaults run more often than maxima
  3. Depth bounds shape, not size
  4. Fewer connections is a cap too
  5. Measure, log-only, then enforce

basics

~20 s

Budget the product, not each field. Caps that look reasonable in isolation compose multiplicatively, so shrink defaults and maxima with depth, decide per field whether a nested connection is offered at all, and roll any tightening out behind usage telemetry.

solid answer

~50 s

Three connections down a path, each capped at a hundred, permits a million leaf objects from one document — so caps have to be chosen as a composed budget rather than field by field. Start with the schema: every connection you declare is a multiplier you have offered, and a naturally bounded relation should be a plain capped list instead. Then set **defaults** deliberately, because most documents omit the inner argument and the default is what actually multiplies. Then graduate the maxima — generous at the root, tighter the deeper a field sits, since a level-three cap is multiplied by the two above it. A depth limit is not a substitute: a 19-level chain of single objects returns nineteen nodes while a three-level document of 23 x 37 x 14 returns 11,914. Finally, never tighten blind: measure the requested page sizes per field per client, run log-only, then enforce.

go deeper

for a junior

Take away the arithmetic: nested page sizes multiply, so a limit that looks generous on one field can permit an enormous response once it sits under two other paged fields.

for a middle

Be able to compute the permitted worst case for a document by multiplying the page sizes along each path, using the server default wherever the argument is omitted, and to say which level contributes most.

for a senior

Show how you would introduce a limit safely on a live graph: per-field, per-client usage data first, a log-only period covering real traffic cycles, then enforcement — and what you would do about clients you cannot redeploy.

for a principal

Own the whole budget. Decide which relations are connections at all, graduate defaults and maxima by depth, add a product-aware backstop, and be explicit that a known document set is the durable answer while caps are the interim.

## The arithmetic nobody does in review Page-size limits are almost always chosen per field, by one person, in isolation. Each one looks defensible: "a hundred is a reasonable maximum for a list". Nesting composes them by multiplication. Three connections down a path, each capped at one hundred, is a permitted worst case of one million leaf objects from one document — and every one of those objects may itself have been resolved, authorized and serialized. Real numbers are usually worse than the ones people picture. In a freight-tracking graph, a document asking for 23 shipments, each with 37 containers, each with 14 scan events, is 11,914 scan-event nodes plus 851 container nodes: a completely ordinary-looking board request, none of whose individual page sizes would raise an eyebrow. ## Depth caps do not bound width The reflex control is a maximum selection depth. It is a useful blast-radius limit and it does not solve this problem at all. A 19-level-deep document that walks a chain of single objects — shipment to consignor to account to owner and onwards — returns nineteen objects. The three-level document above returns nearly twelve thousand. Depth bounds the *shape*; the product of page sizes bounds the *size*. A policy that only limits depth has limited the harmless case. ## Controls, in the order they cost least **The schema itself is the first control.** Every connection you declare is a multiplier you have offered. A relation that is naturally small and bounded — a shipment has at most a dozen legs — should be a plain list with a documented ceiling, not a connection; it removes a multiplier and removes the paging state a client would otherwise have to carry. Deciding *not* to expose a paged field beneath two other paged fields is a legitimate schema decision, and it is far cheaper than policing the documents that would use it. **Defaults, not maxima, are the number that runs in production.** Most documents omit inner `first`. A default of twenty at three levels is eight thousand leaves for a client that specified nothing. Defaults should shrink with depth, because the deeper the field the more parents it is being resolved for. **Graduated maxima.** A single global maximum applied at every level is the wrong instrument, because level three is multiplied by levels one and two and level one is not. Maxima that shrink with depth — generous at the root, tight underneath — bound the product without crippling the common flat query. **A whole-document node budget** as a backstop: the server computes the permitted product from the literal and default page sizes on each path before executing, and rejects the document if it exceeds a ceiling. Turning that into a weighted numeric score per field, with per-field cost hints, is its own discipline; the point here is simply that some control has to look at the *product* rather than at each field alone. ## You cannot tighten a cap blind Caps break clients that were previously served. The rollout, not the number, is the hard part. Measure first: per field, the distribution of requested page sizes and of rows actually returned, broken down by client. The interesting statistic is the high percentile, because that is what you would be rejecting. Then run the new limit in log-only mode long enough to cover the weekly and monthly traffic shapes, and only then enforce. Give that discipline real weight if you have paid for skipping it once. A team that reshaped a nested scan-event list into a connection and renamed the field in the same release shipped it against a mobile build that could not be updated for six weeks; the lesson generalizes exactly to caps, because a pinned client that hard-codes `first: 250` fails the same way the moment the maximum drops to 50 — instantly, on devices you do not control, with no deploy available to you. Anything that can reject a request a pinned client already sends belongs behind telemetry and a log-only period. ## Caps are not free wins A tight nested cap converts one large response into many small round trips. For a mobile client on a poor link that is often *worse* for the user and no cheaper for you in total. The right target is the tail — the handful of documents that would have produced the million-node response — not the median board request, which is the one your product actually depends on. ## The strategic move The durable answer to "bound the product" is to stop accepting arbitrary documents. When the set of documents that reaches production is known and registered, the worst-case product is not estimated, it is enumerated: you can compute it per document at build time, review the outliers, and refuse to register a new one that exceeds the budget. That converts a runtime guessing game into a review-time property, and it is the reason large graphs converge on a known document set. Until then, graduated defaults plus a product-aware backstop is the honest interim, and knowing that it *is* an interim is part of the answer.

  • Why is a maximum selection depth a poor proxy for response size?
    Because depth describes shape and the product describes size. A nineteen-level chain of single objects returns nineteen nodes and is harmless; a three-level document whose connections are 23, 37 and 14 wide returns nearly twelve thousand and passes any depth limit comfortably. Depth is worth having as a blast-radius limit for cyclic schemas, but it does not bound what a nested connection can fetch.
  • How do you pick a new maximum for a field that already has traffic?
    From the observed distribution of requested and returned page sizes for that field, split by client, and specifically its high percentile — that is the traffic the cap would start rejecting. Set the limit above it, run it in log-only mode long enough to cover the weekly and monthly shapes, review what would have been rejected, and only then enforce.
  • What makes a known, registered set of documents a stronger control than any per-field cap?
    It converts an estimate into an enumeration. When the documents that can reach production are known, the worst-case product of each one is computable at review time rather than guessed at runtime, outliers can be refused before they ship, and the caps become a backstop instead of the primary defence. It costs a registration workflow, which is the trade.
  • When is a tighter nested cap the wrong answer for the user?
    When it converts one adequate response into many round trips for a client on a poor link. The cap should target the tail — the rare documents that would produce enormous responses — not the median screen your product depends on. Clamping the common case usually moves cost around rather than removing it, and makes the client chattier.

saying these in an interview costs you the question

  • Applies one maximum page size at every level
  • Treats a depth limit as a size limit
  • Tightens a cap without measuring current usage
  • Sets maxima carefully and ignores defaults
  • Assumes pinned clients can simply be updated
  • Adds nested connections without counting the product

context