Skip to content

Commit 3355470

Browse files
committed
feedback
1 parent 479b719 commit 3355470

File tree

2 files changed

+67
-35
lines changed

2 files changed

+67
-35
lines changed

website/pages/docs/_meta.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const meta = {
2323
'cursor-based-pagination': '',
2424
'custom-scalars': '',
2525
'advanced-custom-scalars': '',
26-
'query-complexity-controls': '',
26+
'operation-complexity-controls': '',
2727
'n1-dataloader': '',
2828
'resolver-anatomy': '',
2929
'graphql-errors': '',

website/pages/docs/query-complexity-controls.mdx renamed to website/pages/docs/operation-complexity-controls.mdx

Lines changed: 66 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
---
2-
title: Query Complexity Controls
2+
title: Operation Complexity Controls
33
---
44

5-
# Query Complexity Controls
5+
# Operation Complexity Controls
66

77
GraphQL gives clients a lot of flexibility to shape responses, but that
88
flexibility can also introduce risk. Clients can request deeply nested fields or
9-
large volumes of data in a single query. Without controls, these operations can slow
9+
large volumes of data in a single operation. Without controls, these operations can slow
1010
down your server or expose security vulnerabilities.
1111

12-
This guide explains how to measure and limit query complexity in GraphQL.js
12+
This guide explains how to measure and limit operation complexity in GraphQL.js
1313
using static analysis. You'll learn how to estimate the cost
14-
of a query before execution and reject it if it exceeds a safe limit.
14+
of an operation before execution and reject it if it exceeds a safe limit.
15+
16+
<Callout type="info" emoji="ℹ️">
17+
In production, we recommend using [trusted documents](https://graphql.org/learn/persistence/)
18+
rather than analyzing arbitrary documents at runtime. Complexity analysis can still be
19+
useful at build time to catch expensive operations before they're deployed.
20+
</Callout>
1521

1622
## Why complexity control matters
1723

@@ -25,28 +31,43 @@ Without safeguards, clients could:
2531
- Use recursive fragments to multiply field resolution
2632
- Exploit pagination arguments to retrieve excessive data
2733

28-
Query complexity controls help prevent these issues. They allow you to:
34+
Certain field types (e.g., lists, interfaces, unions) can also significantly
35+
increase cost by multiplying the number of values returned or resolved.
36+
37+
Complexity controls help prevent these issues. They allow you to:
2938

3039
- Protect your backend from denial-of-service attacks or accidental load
3140
- Enforce cost-based usage limits between clients or environments
3241
- Detect expensive queries early in development
42+
- Add an additional layer of protection alongside authentication, depth limits, and validation
43+
44+
For more information, see [Security best practices](https://graphql.org/learn/security/).
3345

34-
## Estimating query cost
46+
## Estimating operation cost
3547

3648
To measure a query's complexity, you typically:
3749

38-
1. Parse the incoming query into a GraphQL document.
50+
1. Parse the incoming operation into a GraphQL document.
3951
2. Walk the query's Abstract Syntax Tree (AST), which represents its structure.
4052
3. Assign a cost to each field, often using static heuristics or metadata.
41-
4. Reject or log the query if it exceeds a maximum allowed complexity.
53+
4. Reject or log the operation if it exceeds a maximum allowed complexity.
4254

4355
You can do this using custom middleware or validation rules that run before execution.
44-
No resolvers are called unless the query passes these checks.
56+
No resolvers are called unless the operation passes these checks.
57+
58+
<Callout type="info" emoji="ℹ️">
59+
Fragment cycles or deep nesting can cause some complexity analyzers to perform
60+
poorly or get stuck. Always run complexity analysis after validation unless your analyzer
61+
explicitly handles cycles safely.
62+
</Callout>
4563

4664
## Simple complexity calculation
4765

48-
The `graphql-query-complexity` package calculates query cost by walking the AST. Here's a
49-
simple example using `simpleEstimator`, which assigns a flat cost to every field:
66+
There are several community-maintained tools for complexity analysis. The examples in this
67+
guide use [`graphql-query-complexity`](https://github.com/slicknode/graphql-query-complexity) as
68+
an option, but we recommend choosing the approach that best fits your project.
69+
70+
Here's a basic example using its `simpleEstimator`, which assigns a flat cost to every field:
5071

5172
```js
5273
import { parse } from 'graphql';
@@ -82,13 +103,13 @@ console.log(`Query complexity: ${complexity}`);
82103

83104
In this example, every field costs 1 point. The total complexity is the number of fields,
84105
adjusted for nesting and fragments. The complexity is calculated before execution begins,
85-
allowing you to reject costly queries early.
106+
allowing you to reject costly operations early.
86107

87108
## Custom cost estimators
88109

89110
Some fields are more expensive than others. For example, a paginated list might be more
90111
costly than a scalar field. You can define per-field costs using
91-
`fieldExtensionsEstimator`.
112+
`fieldExtensionsEstimator`, a feature supported by some complexity tools.
92113

93114
This estimator reads cost metadata from the field's `extensions.complexity` function in
94115
your schema. For example:
@@ -119,6 +140,12 @@ const UserType = new GraphQLObjectType({
119140
In this example, the cost of `posts` depends on the number of items requested (`limit`) and the
120141
complexity of each child field.
121142
143+
<Callout type="info" emoji="ℹ️">
144+
Most validation steps don't have access to variable values. If your complexity
145+
calculation depends on variables (like `limit`), make sure it runs after validation, not
146+
as part of it.
147+
</Callout>
148+
122149
To evaluate the cost before execution, you can combine estimators like this:
123150
124151
```js
@@ -158,18 +185,14 @@ console.log(`Query complexity: ${complexity}`);
158185
```
159186
160187
Estimators are evaluated in order. The first one to return a numeric value is used
161-
for a given field.
162-
163-
This fallback approach allows you to define detailed logic for specific fields and use
164-
a default cost for everything else.
188+
for a given field. This lets you define detailed logic for specific fields and fall back
189+
to a default cost elsewhere.
165190
166191
## Enforcing limits in your server
167192
168-
To enforce complexity limits automatically, you can use `createComplexityRule` from
169-
the same package. This integrates with GraphQL.js validation and prevents execution of
170-
overly complex queries.
171-
172-
Here's how to include it in your server's execution flow:
193+
Some tools allow you to enforce complexity limits during validation by adding a rule
194+
to your GraphQL.js server. For example, `graphql-query-complexity` provides a
195+
`createComplexityRule` helper:
173196
174197
```js
175198
import { graphql, specifiedRules, parse } from 'graphql';
@@ -207,24 +230,33 @@ const result = await graphql({
207230
console.log(result);
208231
```
209232
210-
If the query exceeds the defined complexity limit, GraphQL.js will return a validation
211-
error and skip execution.
233+
<Callout type="info" emoji="ℹ️">
234+
Only use complexity rules in validation if you're sure the analysis is cycle-safe.
235+
Otherwise, run complexity checks after validation and before execution.
236+
</Callout>
237+
238+
## Complexity in trusted environments
239+
240+
In environments that use persisted or precompiled operations, complexity analysis is still
241+
useful, just in a different way. You can run it at build time to:
212242
213-
This approach is useful when you want to apply global complexity rules without needing
214-
to modify resolver logic or add separate middleware.
243+
- Warn engineers about expensive operations during development
244+
- Track changes to operation cost across schema changes
245+
- Define internal usage budgets by team, client, or role
215246
216247
## Best practices
217248
218-
- Set conservative complexity limits at first, and adjust them based on observed usage.
219-
- Use field-level estimators to better reflect real backend cost.
220-
- Log query complexity in development and production to identify inefficiencies.
221-
- Apply stricter limits for public or unauthenticated clients.
222-
- Combine complexity limits with depth limits, persisted queries, or operation
223-
whitelisting for stronger control.
249+
- Use trusted documents in production when possible.
250+
- Use complexity analysis as a development-time safeguards.
251+
- Avoid running untrusted operations without additional validation and cost checks.
252+
- Account for list fields and abstract types, which can significantly increase cost.
253+
- Avoid estimating complexity before validation unless you're confident in your tooling.
254+
- Use complexity analysis as part of your layered security strategy, alongside depth limits,
255+
field guards, and authentication.
224256
225257
## Additional resources
226258
227-
- [`graphql-query-complexity`](https://github.com/slicknode/graphql-query-complexity): A static analysis tool for measuring query cost in GraphQL.js servers
259+
- [`graphql-query-complexity`](https://github.com/slicknode/graphql-query-complexity): A community-maintained static analysis tool
228260
- [`graphql-depth-limit`](https://github.com/graphile/depth-limit): A lightweight tool to restrict the maximum query depth
229261
- [GraphQL Specification: Operations and execution](https://spec.graphql.org/draft/#sec-Language.Operations)
230262
- [GraphQL.org: Security best practices](https://graphql.org/learn/security/)

0 commit comments

Comments
 (0)