Paste your query. See what it actually costs.
Real depth and complexity, computed by actually parsing your GraphQL query, not by making you count fields and type numbers in by hand. Everything runs in your browser.
How QueryWeight works
- 1
Paste your GraphQL query
Drop in any query, mutation, or subscription. It is parsed client-side into an abstract syntax tree, the same way a GraphQL server reads it, so you get the real structure rather than an approximation you typed by hand.
- 2
Depth is measured from the AST
QueryWeight walks the tree and records the deepest chain of nested fields. A top-level field is depth 1, its children depth 2, and so on. Fragment spreads are expanded in place so they count toward the real nesting.
- 3
Each field gets a cost
Every field starts at a base cost of 1. Fields with a first/last/limit argument become multipliers using that number; list-shaped fields without one use your assumed page size. A field's cost is multiplied by every list ancestor above it.
- 4
Unbounded lists are flagged
Any list or connection field selecting sub-fields without a first/last argument is flagged separately, because those are exactly the fields that make total cost impossible to bound at runtime.
- 5
The breakdown shows what is expensive
The most expensive fields are ranked by cost with a share-of-total bar, so it is obvious which nested list is driving the number and what to trim first.
- 6
Check against a budget
Enter an optional complexity limit, your server's threshold or a public API's published budget, and QueryWeight tells you whether the query fits and by how much it is over if it does not.
What each result means
QueryWeight reports four things. Depth and complexity are the two numbers a server-side limiter enforces; the flags and breakdown tell you where the cost comes from.
The longest chain of nested fields in the query. Depth limiting is the simplest defence against a malicious infinitely nested query, and many servers reject anything past a fixed depth (often 10-15) before they even bother scoring complexity.
query { # depth 0
viewer { # depth 1
repositories { # depth 2
nodes { # depth 3
name # depth 4 <- max depth = 4
} } } }The total estimated cost: the sum of every field's cost, where list fields multiply their subtree by a page size. This is the number a server-side complexity limiter compares against your configured threshold to accept or reject the query.
search(first: 50) { # 50
nodes { # 50
... on Repository {
name # 50
} } }
# a single first:50 makes every child cost 50List or connection fields that select sub-fields but carry no first/last argument. These are the highest-risk fields: without a page cap, the server has no bound on how many objects the field resolves, so the assumed page size is a guess, not a guarantee.
repository(name: "x") {
issues { # <- no first/last, unbounded
nodes { title }
}
}A ranked breakdown of which fields contribute the most to the total, each with its share of the score. Because nested list multipliers compound, one deep field often dominates. Trimming it is almost always the fastest way back under budget.
comments(first: 30) { # cost 30
nodes {
reactions(first: 10) { # cost 30 x 10 = 300 <- top
nodes { content }
} } }The complexity model, in full
The estimator is deliberately simple and transparent, so you can predict what any query will score. These are the exact rules QueryWeight applies.
# 1. Every field has a base cost of 1.
# 2. A field is a LIST field (a multiplier) when either:
# - it has a first / last / limit argument -> use that integer
# - it selects sub-fields and its name looks
# like a collection (plural, nodes, edges,
# items, results, connection) -> assumed page size
# 3. A field's cost = 1 x (page sizes of all list ancestors).
# 4. Total complexity = the sum of every field's cost.
query {
user { # 1
friends(first: 10) { # 10
name # 10
posts(first: 5) { # 10 x 5 = 50
title # 50
}
}
}
}
# total = 1 + 10 + 10 + 50 + 50 = 121A page size taken from a variable (for example first: $count) is unknown at analysis time, so the assumed page size is used and the field is flagged. This mirrors how a real server treats a query it has not executed yet.
When to use QueryWeight
| Situation |
|---|
| Reviewing a PR that adds a nested query |
| Sizing a query for GitHub's GraphQL API |
| Checking a Shopify Admin API call |
| Designing pagination on a new schema |
| Setting a server complexity limit |
| Teaching why nesting is costly |
Frequently Asked Questions
What is GraphQL query complexity and why does it matter?
GraphQL query complexity is a single number that estimates how expensive a query is to resolve. Unlike REST, where each endpoint has a roughly fixed cost, a single GraphQL query can request arbitrarily deep and wide data, so two queries against the same schema can differ in cost by orders of magnitude.
That flexibility is also the risk: an attacker (or an accidental infinite nesting bug) can send a small query string that forces the server to resolve millions of objects. Complexity analysis puts a bound on that. Every major GraphQL server library ships some form of it, which is why knowing a query's number before it hits production is genuinely useful.
How does QueryWeight calculate the complexity score?
Every field starts with a base cost of 1. A field is treated as a list field (a multiplier) when it carries a first, last, or limit argument, or when it selects sub-fields and its name looks like a collection (plural, or nodes/edges/items).
The cost of any field is multiplied by the page sizes of every list ancestor above it, so nesting compounds. The total complexity is the sum of every field's cost.
users(first: 5) { # 5
posts(first: 3) { # 5 x 3 = 15
title # 15
}
}
# total = 5 + 15 + 15 = 35Does this replace server-side complexity limiting?
No, and it is not meant to. QueryWeight is an estimator that runs against the query text alone, with no access to your schema, your real page sizes, or your resolver costs. It is for fast sanity checks: reviewing a PR, sizing a query before you send it, or teaching how nesting compounds cost.
Your server still needs a real complexity limiter (graphql-query-complexity, graphql-ruby's analyzer, or your framework's built-in) wired into the request pipeline, because that is the only place the limit is actually enforced with your true schema costs.
What is a reasonable complexity limit to set?
There is no universal number, because it depends on your assigned per-field costs and your real page sizes. As reference points, public APIs publish their own budgets: GitHub's GraphQL API uses a node-based limit of 500,000 nodes per hour with a per-query cap, and Shopify's Admin API uses a per-request cost bucket with a leaky-bucket refill.
A common starting point for a self-hosted API is a per-request limit in the low thousands, then tuning it down as you observe real traffic. Use the complexity limit field in QueryWeight to test a query against whatever threshold you pick.
Is my query sent to a server?
No. QueryWeight parses and scores your query entirely in your browser with a client-side parser. Nothing is uploaded, there is no backend, and there are no network calls on the query you paste. You can use it offline once the page has loaded.
How do I bring an over-budget query back under the limit?
Start with the most expensive field in the breakdown, because a single deeply nested list field usually dominates the total. The two highest-leverage fixes are lowering a first/last value and removing a level of nesting.
# before: 20 x 30 x 10 = 6000 on the deepest field
issues(first: 20) { nodes { comments(first: 30) { nodes {
reactions(first: 10) { nodes { content } } } } } }
# after: cap pages and drop the reactions level
issues(first: 10) { nodes { comments(first: 10) { nodes {
body } } } }Does it handle fragments and named operations?
Yes. Named operations, variables, aliases, directives, inline fragments, and fragment spreads all parse. Fragment spreads are expanded in place so their fields count toward depth and complexity, and recursive fragments are guarded against so the tool will not hang.
If a document contains more than one operation, QueryWeight analyses the first one and tells you it did, since only one operation executes per request anyway.