API, Data & Developer Tools

GraphQL Query Complexity Estimator

Parse a GraphQL operation and its fragments, apply configurable field and list weights, and see depth, alias count and a transparent cost breakdown per field.

  • Cost tree
  • Depth and alias totals
  • Limit verdict
Runs in your browser

Everything you paste, type or drop is processed in this browser tab. It is not uploaded, logged, stored or sent to analytics.

GraphQL complexity workspace

1 Operation and schema

Example:

With the schema, list fields are known exactly and unknown fields are reported. Without it, list fields are guessed from size arguments and plural names.

2 Weights and limits

Variables, overrides and operation name

3 Cost breakdown

Paste an operation, or load the example, then choose Estimate cost.

What the GraphQL Query Complexity Estimator does

This estimator parses a GraphQL operation with its fragments and works out how expensive it is to answer: query depth, the number of aliases, and a weighted cost in which every list multiplies the work beneath it. Each field's contribution is shown, so the total can be checked line by line rather than trusted.

GraphQL lets a client ask for a great deal in one request. Twenty aliased copies of a field, or three nested lists of 50, look small on screen and fan out into thousands of resolver calls. Servers defend with depth limits and cost limits; this page lets you see where a query stands against the limits you choose before it reaches one.

How to use it

  1. Paste the operation, including any fragments it spreads. If the document has several operations, name the one to cost under the advanced options.
  2. Paste the schema SDL too if you can. It tells the estimator which fields return lists and lets it report unknown fields, missing required arguments and misplaced selections.
  3. Set the weights and limits to match your server - or keep the defaults: 1 per object field, 0 per leaf, 10 items for a list with no size argument, and limits of cost 1,000, depth 10 and 15 aliases.
  4. Add variables as JSON when sizes come from variables, and per-field overrides such as Query.search = 10 for resolvers you know are expensive.
  5. Choose Estimate cost, read the verdicts, then use the cost tree to find the field that dominates the total.

Reading the results

Subtree cost is a field's own weight plus its multiplier times everything selected under it, so the top-level rows add up to the total. The field with the largest subtree is where a limit or a smaller page size will have the most effect.

Multiplier comes from a size argument when there is one (first, last, limit and similar), from its variable or the variable's default, and otherwise from the assumed list size. A Relay connection passes its first to edges or nodes, so the size is not counted twice.

Fields inside inline fragments on a union or interface are all added, as if every branch were returned. That is an upper bound, which is what a cost limit should enforce.

Worked example: a bookshop listing page

The first example asks for books(first: $first) with $first defaulting to 20, each book's card fields through a BookCard fragment, its first 5 reviews with the reviewer's name, the page info, and an aliased search for 3 results with an inline fragment per result type.

Working up from the leaves with the default weights: a review is 1 (its author object) under a reviews field of weight 1 with multiplier 5, so reviews costs 1 + 5 x 1 = 6. A book node costs 1 + 1 (author) + 6 = 8. edges costs 1 + 20 x 8 = 161, books costs 1 + 161 + 1 (pageInfo) = 163, and search costs 1 + 3 x 1 = 4 because Author.books(first: 2) contributes 1. The total is 167, at depth 6 with 1 alias - within every default limit.

The second example repeats book(id) twenty times under aliases, each with 50 reviews whose authors list their own reviews. Each copy costs 1 + (1 + 50 x (1 + (1 + 10 x 0))) = 102, so the total is 2,040 with 20 aliases: it fails both the cost and alias limits while staying only 5 levels deep - exactly why depth limits alone are not enough.

Formulas and scoring rules

Field cost
cost(field) = weight(field) + multiplier(field) x sum(cost(child) for each selected child)Leaves have no children, so their cost is their weight.
Weight
weight = override(Type.field) if set, else objectWeight (default 1) for fields with a selection set, else scalarWeight (default 0)
Multiplier
multiplier = size argument value (first, last, limit, pageSize, perPage, top, count, take, size) ; else defaultListSize (default 10) for list fields ; else 1A sized field that is not a list passes its size to its edges or nodes list instead.
Operation cost
total = sum(cost(root field)) over the operation's root selections, with fragments expanded in placeNo rounding; fractional weights are shown to two decimals.
Depth
depth = the largest number of nested fields on any path; fragments and inline fragments add no level

This cost model is a convention, not a standard

The GraphQL specification defines the language and execution, not what a query costs. The formula here is the widely used "weight plus multiplier times children" approach found in several server libraries and public APIs, with the defaults stated on the page. Your server may count differently - some charge per returned node, some add a base cost per request, some weigh mutations more - so set the weights to match it and treat the number as an estimate.

What the page does implement from the specification is parsing (section 2, Language) and a set of validation rules: fields must exist on their type, required arguments must be given, leaves take no selection set and objects need one, fragments must exist and not form cycles, and every variable used must be declared.

Limitations: what the result does not prove

  • It estimates work from the query text. Real cost depends on resolvers, caching, batching (DataLoader) and data size, none of which a query can reveal.
  • Without the schema, list fields are guessed from size arguments and plural names, so the estimate can be too low or too high; the hero line says when that happened.
  • Custom directives such as @cost or @listSize in your SDL are not read. Put the same numbers in the per-field overrides.
  • The operation is only parsed. Nothing is sent to a GraphQL endpoint, so server-side limits and persisted-query rules are not checked.

Privacy: where your data goes

Everything you paste, type or drop is processed in this browser tab. It is not uploaded, logged, stored or sent to analytics. Session recording and tag-manager scripts are switched off on this page.

Standards and sources

Frequently asked questions

What is a good maximum query cost for a public GraphQL API?

There is no universal number; it depends on your weights. Cost your heaviest legitimate screens with this page, set the limit comfortably above the largest of them, and log rejections so you can raise it deliberately. Public APIs typically pair a per-query limit with a per-client budget over time.

Why is a depth limit not enough to stop expensive GraphQL queries?

Because cost grows with breadth as well as depth. The alias example is only five levels deep but asks for twenty copies of the same expensive field. Aliases and large list sizes multiply work without adding depth, so limit cost and aliases too.

How does it treat Relay connections with edges and node?

When a field with a size argument such as first: 20 returns a connection object rather than a list, that size is applied to its edges or nodes list. The connection itself is counted once, so the page size is not multiplied twice.

Do fragments change the cost of a GraphQL query?

No. Named fragments and inline fragments are expanded into the fields they contain, which is how a server executes them. They cost exactly what the same fields would cost written out, and they add no depth.

What happens when a size argument is a variable without a value?

The variable's default is used if the operation declares one. Otherwise the assumed list size is used and the Why column says so. Supply the variables JSON to cost the query exactly as a client would send it.

Can I make some resolvers more expensive than others?

Yes. Add lines such as Query.search = 10 or Book.reviews = 3 under Per-field weights. The override replaces the default weight for that field wherever it appears, and still multiplies with any list above it.

Last reviewed by the A2Z.Tools team against the sources listed above.

Rate this tool

Was this tool useful? Your feedback helps us improve it.

No ratings yet — be the first to rate this tool.
Your rating (required)
0 / 2000

Please do not include passwords, payment details or other sensitive information.

Your feedback is sent privately to the A2Z.Tools team and will not be posted publicly.