Review GraphQL Query Cost Limits With Adversarial Documents

Set a cost budget, test nested fragments and alias inflation, and keep the throttle decision with a named company owner.

Review GraphQL Query Cost Limits With Adversarial Documents
WORK_LANE / FIELD GUIDE / QUALITYREVIEW QUEUEACCEPTANCEPASSowner review requiredEVIDENCE TRAILTICKET → BRANCH → REVIEW → CLIENT RELEASE
On this page
  1. Quick read
  2. Readiness check
  3. Publish the cost model before the limit
  4. Scope the review to one schema surface
  5. Attack the limit with documents a normal client would not send
  6. Enforce once, at a point you can explain
  7. Compare computed cost with observed work
  8. Test batching, caching, and partial failure
  9. Review cadence and measures with denominators
  10. Close with a calculator, a test set, and a rollout record
  11. Role brief
  12. Frequently asked questions
  13. Sources

Is the handoff ready?

Use these checks before access expands or the first ticket begins.

Model

Ready: Field weights, depth rule, and list multiplier written

Needs work: A request count limit only

Budget

Ready: Named per client and per operation limit

Needs work: One global number nobody owns

Tests

Ready: Adversarial documents and expected rejection codes

Needs work: One normal query from the frontend

Rollout

Ready: Shadow mode, then enforce, with owner sign off

Needs work: Immediate production block

Publish the cost model before the limit

A GraphQL limit without a published model becomes an arbitrary rejection that nobody can predict. Write the weight of each field, the multiplier for list fields, the depth ceiling, and the maximum page size. Put the document in the repository next to the schema.

The model does not have to be perfect. It has to be stable, testable, and explainable to a client team. A Philippines-based programmer can implement a deterministic calculator and show the same input producing the same cost.

Decide whether the budget applies per operation, per client, or per user session. A generous per operation limit with no per client budget still allows one client to consume the pool.

Name the unit of work the cost represents. If a weight of one means a single indexed lookup, say so and check a few resolvers against that definition. Weights copied from a blog post without a unit become numbers that nobody can defend when a client asks why a query was refused.

Scope the review to one schema surface

The ticket names the schema version, the gateway, the resolver set under review, the calculator location, the rejection status, and the owner of the client allowlist. Ask for a test command and a table of cost against measured work.

Start with the public read surface. Mutations and subscriptions have different failure modes and should follow in a second ticket once the read limit is stable.

State the rollout order: log only, then reject above a high threshold, then tighten. Each stage needs an observation window and a named person who decides to move on.

Attack the limit with documents a normal client would not send

Alias inflation duplicates the same field many times in one document and multiplies resolver work without increasing apparent depth. Nested fragments spread the cost across files that look small in isolation. Wide list fields with large page sizes multiply everything beneath them.

Ask the programmer to build a document set with expected costs and expected outcomes. The set should include one document just under the budget and one just over it, so the boundary is proven rather than assumed.

Test introspection and schema queries under the same rule. A limit that exempts introspection leaves a cheap way to do expensive work.

  • Alias inflation on a list field with a page size at the maximum.
  • Fragment chain that repeats a costly subtree at depth.
  • Query just below and just above the declared budget.
  • Introspection query under the same calculator.

Enforce once, at a point you can explain

Enforcement usually belongs at the gateway, before execution, using the parsed document and the schema metadata. Resolver level checks arrive after work has started and produce partial responses that are hard to reason about.

If the gateway cannot see variable values that affect cost, such as page size, the calculator must use the declared maximum rather than the supplied value. Otherwise a client can send a small variable and a large effective query.

Rejections need a stable error code and a message that names the budget and the computed cost. That message is part of the contract, not a debugging convenience.

Compare computed cost with observed work

For each test document, record the computed cost, the decision, the response time, the resolver count, and the database query count. A calculator that rejects the right documents but misprices the accepted ones will still allow a costly query through.

Keep the raw query document hash and the calculator version beside the result. When weights change, the old result should be explainable rather than mysterious.

Report the false rejection case too. A legitimate dashboard that trips the limit is a product problem, and the client owner needs to see it before enforcement widens.

Test batching, caching, and partial failure

Batched operations can hide several expensive documents behind one transport request. Astute clients also retry after a rejection, so the limit must not create a retry loop that increases load.

Persisted queries change the calculation surface. If the gateway trusts a stored hash without rechecking cost, a previously accepted document can outlive a budget change.

Test the case where the calculator throws. A failure in cost estimation should fail closed with a clear error, not fall through to execution.

Check the header and transport layer too. A client that can request compression, batching, or a different content type may reach a path where the parsed document differs from the one the calculator measured, and that gap is worth one adversarial test.

  • Batched request containing two documents near the budget.
  • Retry storm after a rejection.
  • Persisted document accepted before a weight change.
  • Calculator exception during parsing.

Review cadence and measures with denominators

Measure rejected operations divided by all operations, costly accepted operations divided by all accepted operations, and p95 latency for accepted operations. Segment by client so one heavy consumer is visible.

Track the count of documents above the old budget that were accepted before enforcement. That number is the backlog the limit is meant to address.

Review monthly and after any schema change that adds a list field or a new resolver fan out. A cost model that is not revised with the schema decays into a formality.

Close with a calculator, a test set, and a rollout record

The handoff contains the cost document, the calculator revision, the adversarial document set, the observed decisions, the false rejection list, and the rollout stage with its owner. It also names the next schema review.

Accept the work when a reviewer can compute the cost of a new document by hand from the published model and match the gateway decision.

Record which clients are exempt and why. An exemption list that lives only in a gateway configuration file is invisible to the next reviewer.

Put the operating agreement in writing.

Surface

One schema version and its public read operations.

Model

Field weights, list multiplier, depth ceiling, max page size.

Tests

Adversarial documents with expected cost and rejection code.

Rollout

Company owner approves each enforcement stage.

Frequently asked questions

Is a request rate limit enough for GraphQL?

No. One expensive document can do the work of many cheap requests. Cost needs its own model and budget.

Should the limit reject or truncate?

Reject with a stable error code. Silent truncation changes the meaning of the response and hides load.

Who approves a budget increase?

The company owner of the API, after seeing the client, the cost, and the capacity impact.

Sources

  1. OWASP Application Security Verification Standard

    Input handling and resource consumption verification topics.

  2. NIST Secure Software Development Framework

    Practices for designing and reviewing exposed interfaces.

  3. OpenTelemetry documentation

    Instrumentation for resolver counts, latency, and rejection metrics.

  4. GitHub pull request reviews

    Review mechanics for the calculator and schema changes.

  5. Google Technical Writing

    Guidance for a readable cost model document.

Build a programming lane your technical owner can review.

Bring the stack, backlog, acceptance checks, and release boundaries. We will help scope the role around the work.

Discuss your programming needs