practice

Query Cost Analysis

also called Query Complexity Limiting, Cost-Based Rate Limiting

Computing the resource cost of a client-specified query before executing it, so that an API which lets clients define their own requests cannot be asked to do unbounded work.

graphqlrate-limitingprotectionapi-designcapacity

In a conventional REST API the platform decides what each endpoint costs. In a query language the client decides, by choosing depth, breadth and connections. Without a cost model, a single request can be arbitrarily expensive, and the platform has handed an unbounded resource lever to anyone with an API key.

Query cost analysis assigns a cost to each field and connection, computes the total statically before execution, and rejects anything over budget.

Why it must exist before launch

The failure mode is a single query taking down a shared backend, which is severe enough. The deeper problem is that adding cost limits after clients have built on unbounded queries is a breaking change. Clients will have written queries that exceed any sane budget, those queries work today, and constraining them breaks integrations.

Platforms that add governance after the first incident face a choice between an ongoing reliability risk and an ecosystem-breaking change. Neither is good, and both are avoidable by shipping the cost model on day one.

Implementation patterns

  • Static cost from the query document, computed before any resolver runs. Cost estimated from requested page sizes and nesting rather than from actual results, so the rejection happens before work begins.
  • Cost-based rate limiting rather than request counting. "1000 requests per minute" is meaningless when one request can be a thousand times heavier than another; a cost budget per window is the meaningful unit.
  • Depth and node limits as a cheap backstop against pathological nesting, including cyclic traversals.
  • Per-client cost budgets with different shapes, so an interactive client gets burst capacity and a batch client gets sustained throughput.
  • Cost returned in the response, so clients can see what their queries consume and optimise deliberately rather than by trial and error.
  • Persisted queries for first-party clients: a fixed set of pre-registered, pre-costed queries, eliminating unbounded cost entirely for traffic you control.

Industry example

A commerce platform exposing a rich, interconnected data model to a large third-party developer ecosystem is the archetype. The client benefit is genuine — one query replaces several round trips, which matters enormously on mobile — and the platform's exposure is equally genuine, because every app developer can now write a query against a shared backend.

The mature configuration combines a published cost formula, cost-based limits visible in every response, persisted queries for the platform's own applications, and per-connection page size caps that bound the cost of any single traversal.

Alongside cost analysis sits the other structural obligation such platforms acquire: the N+1 problem becomes mandatory to solve rather than an optimisation. A query for 100 orders each with a customer naively produces 101 database queries, and one unbatched resolver silently reintroduces the problem platform-wide.

Failure scenarios

  • Cost computed after execution, which measures the damage rather than preventing it.
  • Costs that do not reflect reality — a field marked cheap that triggers an expensive downstream call, so the model gives false confidence.
  • Introspection unbounded, letting a client walk the whole schema expensively.
  • No cost visibility for clients, so developers cannot optimise and simply retry until something works.
  • Limits applied uniformly, throttling a large legitimate customer at the same threshold as a hobby project.

Trade-offs

A cost model is an approximation and will sometimes be wrong in both directions — rejecting a query that would have been cheap, or admitting one that turns out expensive. Keeping it accurate is ongoing work as the schema and the underlying data change.

It also adds a concept developers must learn, which is a real developer-experience cost. The mitigation is transparency: publish the formula, return the cost, and let developers reason about it rather than guess.

Interview question

"You are launching a GraphQL API for third-party developers. Before any client connects, what protections must exist — and why can you not add them later?"