@nxgt/graphql-validation 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -6
- package/dist/bad-user-input.d.ts +17 -2
- package/dist/bad-user-input.d.ts.map +1 -1
- package/dist/builder/check-constraints.d.ts +4 -2
- package/dist/builder/check-constraints.d.ts.map +1 -1
- package/dist/builder/declared.d.ts +12 -0
- package/dist/builder/declared.d.ts.map +1 -0
- package/dist/chunks/{index-xcaf9kes.js → index-atwkcfg3.js} +5 -5
- package/dist/chunks/{index-xcaf9kes.js.map → index-atwkcfg3.js.map} +2 -2
- package/dist/chunks/{index-twevz7yc.js → index-w9pw7fsk.js} +68 -34
- package/dist/chunks/{index-twevz7yc.js.map → index-w9pw7fsk.js.map} +6 -5
- package/dist/cli.js +1 -1
- package/dist/codegen.js +2 -2
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +497 -10
- package/dist/index.js.map +13 -5
- package/dist/limits/cost-context.d.ts +26 -0
- package/dist/limits/cost-context.d.ts.map +1 -0
- package/dist/limits/describe.d.ts +9 -0
- package/dist/limits/describe.d.ts.map +1 -0
- package/dist/limits/introspection.d.ts +12 -0
- package/dist/limits/introspection.d.ts.map +1 -0
- package/dist/limits/list-size.d.ts +23 -0
- package/dist/limits/list-size.d.ts.map +1 -0
- package/dist/limits/max-depth.d.ts +28 -0
- package/dist/limits/max-depth.d.ts.map +1 -0
- package/dist/limits/max-fields.d.ts +35 -0
- package/dist/limits/max-fields.d.ts.map +1 -0
- package/dist/limits/max-list-cost.d.ts +77 -0
- package/dist/limits/max-list-cost.d.ts.map +1 -0
- package/dist/limits/pending-size.d.ts +13 -0
- package/dist/limits/pending-size.d.ts.map +1 -0
- package/dist/with-validation.d.ts.map +1 -1
- package/docs/README.md +1 -0
- package/docs/guide/constraints.md +3 -3
- package/docs/guide/errors.md +12 -3
- package/docs/guide/query-limits.md +311 -0
- package/docs/roadmap.md +9 -0
- package/docs/troubleshooting.md +73 -0
- package/package.json +1 -1
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# Limiting the depth and cost of a query
|
|
2
|
+
|
|
3
|
+
`maxDepth`, `maxListCost` and `maxFields` are graphql-js validation rules. They refuse an
|
|
4
|
+
operation before any resolver runs, so a hostile query costs a walk of its
|
|
5
|
+
document and nothing more. For `@constraint`, see
|
|
6
|
+
[Constraints](constraints.md).
|
|
7
|
+
|
|
8
|
+
## Why
|
|
9
|
+
|
|
10
|
+
A schema with a list cycle lets a client multiply a cost at each level:
|
|
11
|
+
|
|
12
|
+
```graphql
|
|
13
|
+
type Folder { id: ID! items(first: Int = 100): [Item!]! }
|
|
14
|
+
type Item { id: ID! folder: Folder! }
|
|
15
|
+
type Query { folder(id: ID!): Folder }
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```graphql
|
|
19
|
+
query {
|
|
20
|
+
folder(id: "1") {
|
|
21
|
+
items { folder { items { folder { items { folder { items { id } } } } } } }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Four `items` levels of 100 each can ask for 100 million nodes in a query nine
|
|
27
|
+
levels deep (`folder`, four `items`, three `folder`, `id`). `maxDepth` bounds the nesting; `maxListCost` bounds the work.
|
|
28
|
+
|
|
29
|
+
Neither bounds breadth: a query of 2000 aliases of `item(id: "1") { id }` is two
|
|
30
|
+
levels deep and costs 4000, yet selects 4000 fields for the executor to run; so does a
|
|
31
|
+
query of thousands of aliases of `__typename`, which neither rule counts.
|
|
32
|
+
`maxFields` bounds that.
|
|
33
|
+
|
|
34
|
+
## Use
|
|
35
|
+
|
|
36
|
+
With graphql-js, add the rules to the ones `validate` already runs:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { maxDepth, maxFields, maxListCost } from '@nxgt/graphql-validation';
|
|
40
|
+
import { parse, specifiedRules, validate } from 'graphql';
|
|
41
|
+
|
|
42
|
+
const errors = validate(schema, parse(query), [
|
|
43
|
+
...specifiedRules,
|
|
44
|
+
maxDepth({ depth: 8 }),
|
|
45
|
+
maxListCost({ budget: 10_000, defaultFirst: 50 }),
|
|
46
|
+
maxFields({ limit: 500 }),
|
|
47
|
+
]);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
With graphql-yoga, in a plugin:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { maxDepth, maxFields, maxListCost } from '@nxgt/graphql-validation';
|
|
54
|
+
import { createYoga } from 'graphql-yoga';
|
|
55
|
+
|
|
56
|
+
createYoga({
|
|
57
|
+
schema,
|
|
58
|
+
plugins: [
|
|
59
|
+
{
|
|
60
|
+
onValidate: ({ addValidationRule }) => {
|
|
61
|
+
addValidationRule(maxDepth({ depth: 8 }));
|
|
62
|
+
addValidationRule(maxListCost({ budget: 10_000, defaultFirst: 50 }));
|
|
63
|
+
addValidationRule(maxFields({ limit: 500 }));
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Each rule reports **one** `GraphQLError` per operation, whose `nodes` is the
|
|
71
|
+
operation. An invalid option (`depth: 0`, a fractional `budget`, `limit: 0`) throws a
|
|
72
|
+
`RangeError` when the rule is created.
|
|
73
|
+
|
|
74
|
+
## `maxDepth({ depth, ignoreIntrospection? })`
|
|
75
|
+
|
|
76
|
+
The depth of an operation is the nesting of its field selections: a top-level
|
|
77
|
+
field is depth 1.
|
|
78
|
+
|
|
79
|
+
- Named fragments are followed, each measured once. A fragment cycle adds
|
|
80
|
+
nothing (`NoFragmentCyclesRule` reports it).
|
|
81
|
+
- Inline fragments add no depth.
|
|
82
|
+
- `__typename` does not count. `__schema` and `__type` count like any field:
|
|
83
|
+
the standard introspection query (`getIntrospectionQuery()`) is **15 levels
|
|
84
|
+
deep**, so `maxDepth({ depth: 8 })` refuses it. Where introspection is
|
|
85
|
+
disabled, or the client is trusted (GraphiQL in development), pass
|
|
86
|
+
`ignoreIntrospection: true`: `__schema` and `__type` and everything under
|
|
87
|
+
them are then left out.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
maxDepth({ depth: 8, ignoreIntrospection: process.env.NODE_ENV !== 'production' });
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The refusal:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"message": "Operation \"Deep\" is 9 levels deep, more than the 8 allowed",
|
|
98
|
+
"extensions": { "code": "QUERY_TOO_DEEP", "depth": 9, "limit": 8 }
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`depth` is the operation's real depth. An anonymous operation reads
|
|
103
|
+
`Operation is 9 levels deep, ...`.
|
|
104
|
+
|
|
105
|
+
## `maxListCost({ budget, defaultFirst, sizeArguments?, ignoreIntrospection? })`
|
|
106
|
+
|
|
107
|
+
### How cost is counted
|
|
108
|
+
|
|
109
|
+
A field costs 1, times the sizes of the list fields enclosing it. A list field
|
|
110
|
+
multiplies the cost of its sub-selection by its size; a field that is not a list
|
|
111
|
+
multiplies by 1. A list field that selects nothing below it (`tags: [String!]!`)
|
|
112
|
+
costs 1.
|
|
113
|
+
|
|
114
|
+
```graphql
|
|
115
|
+
{ folder(id: "1") { id items(first: 5) { id } } }
|
|
116
|
+
# folder 1 + id 1 + items 1 + 5 * id 1 = 8
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
A type that is a list once unwrapped from non-null is one list, nested
|
|
120
|
+
`[[X]]` included. `__typename` costs nothing.
|
|
121
|
+
|
|
122
|
+
`__schema` and `__type` are costed like any field, their lists sized by the
|
|
123
|
+
schema itself: `__Schema.types` is the number of types of the schema, and
|
|
124
|
+
`fields`, `inputFields`, `enumValues`, `args`, `interfaces`, `possibleTypes`,
|
|
125
|
+
`directives` and `locations` are the longest such list anywhere in the schema.
|
|
126
|
+
A query repeating `a1: __schema { ... }` hundreds of times is refused like any
|
|
127
|
+
other. The standard introspection query costs on the order of 200,000 even on a
|
|
128
|
+
schema of one field, so it is refused by a budget
|
|
129
|
+
of 10,000: pass `ignoreIntrospection: true` where introspection is disabled or
|
|
130
|
+
the client is trusted, such as GraphiQL in development.
|
|
131
|
+
|
|
132
|
+
### How a list is sized
|
|
133
|
+
|
|
134
|
+
A list field is sized by its **size arguments**, `['first', 'last', 'limit']`
|
|
135
|
+
unless `sizeArguments` says otherwise:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
maxListCost({ budget: 10_000, defaultFirst: 50, sizeArguments: ['first', 'pageSize'] });
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Each size argument the field gives or declares a default for says a size, and
|
|
142
|
+
the **largest** wins: `items(first: 0, limit: 1000000)` is a million items. An
|
|
143
|
+
argument says:
|
|
144
|
+
|
|
145
|
+
| Argument | Size |
|
|
146
|
+
| --- | --- |
|
|
147
|
+
| a literal Int, 0 or more (`first: 5`) | that number |
|
|
148
|
+
| a string literal holding a canonical integer (`limit: "100000000"`, for a `Long`-like scalar) | that number: digits only, no sign, no leading zero |
|
|
149
|
+
| a bigint (an SDL default held by a code-first schema, or after graphql-tools' `mapSchema`) | that number |
|
|
150
|
+
| an integral Float literal (`first: 1000.0`, `first: 1e3`) | that number; `1.5` and `-1000.0` are unusable |
|
|
151
|
+
| a negative literal (`first: -1000`), another string | as if unusable, see below |
|
|
152
|
+
| a variable (`first: $n`) | as if unusable: validation has no variable values |
|
|
153
|
+
| not given | its SDL default, when it has one; else nothing |
|
|
154
|
+
|
|
155
|
+
An SDL default is read the same way: `limit: String = "1000"` and `first:
|
|
156
|
+
Float = 1000.0` are sizes of 1000. Every size is held at most
|
|
157
|
+
`Number.MAX_SAFE_INTEGER`.
|
|
158
|
+
|
|
159
|
+
An argument given but unusable counts for its SDL default when it has one in
|
|
160
|
+
any accepted form (`items(first: Int = 100)`), and `defaultFirst` otherwise. On
|
|
161
|
+
an abstract parent that is the largest default, or `defaultFirst` when it is
|
|
162
|
+
larger and some possible type has no default. A
|
|
163
|
+
field where no size argument says anything is sized `defaultFirst`.
|
|
164
|
+
|
|
165
|
+
On an interface or a union, the default is the **largest** over the interface's
|
|
166
|
+
field and the field of every possible type (`Node.kids(first: Int = 1)` next to
|
|
167
|
+
`Folder.kids(first: Int = 100000)` is sized 100,000), since the query cannot
|
|
168
|
+
say which object comes back.
|
|
169
|
+
|
|
170
|
+
A negative number is therefore never taken as a size: no subtree costs less than
|
|
171
|
+
nothing, and `items(first: -1000)` next to an expensive sibling cannot cancel
|
|
172
|
+
the sibling's cost.
|
|
173
|
+
|
|
174
|
+
Pick `defaultFirst` knowing a variable counts for it: a client using
|
|
175
|
+
`first: $n` is charged the default whatever it sends. If your server caps a size
|
|
176
|
+
argument, set `defaultFirst` (or the SDL default) at that cap.
|
|
177
|
+
|
|
178
|
+
### Relay connections
|
|
179
|
+
|
|
180
|
+
On a Relay connection the size is on a field that returns an **object**, and the
|
|
181
|
+
list is below it:
|
|
182
|
+
|
|
183
|
+
```graphql
|
|
184
|
+
type Query { items(first: Int): ItemConnection! }
|
|
185
|
+
type ItemConnection { edges: [ItemEdge!]! nodes: [Item!]! pageInfo: PageInfo! }
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```graphql
|
|
189
|
+
{ items(first: 100) { edges { node { id } } nodes { id } } }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
A field that is not a list and states a size, read as for a list above (a size
|
|
193
|
+
argument given, or declared with a default; on an abstract parent the largest
|
|
194
|
+
over its possible types), hands it down as a **pending size** to the lists
|
|
195
|
+
selected **directly on its return type**:
|
|
196
|
+
|
|
197
|
+
- Only a list that **declares none of the size arguments** takes it, instead of
|
|
198
|
+
`defaultFirst`: `edges` and `nodes` above are both 100. A list that declares
|
|
199
|
+
one (`recent(first: Int)`) reads its own arguments, else `defaultFirst`,
|
|
200
|
+
never the pending size.
|
|
201
|
+
- On an interface, when some possible types declare a size argument on that
|
|
202
|
+
list and others do not, the larger of the pending size and the list's own
|
|
203
|
+
reading is used, since the query cannot say which comes back.
|
|
204
|
+
- It stops at the first field crossed: an object field (`owner`), `pageInfo` or
|
|
205
|
+
a wrapper object passes nothing on, so the lists below them are sized as
|
|
206
|
+
without a connection. In `items(first: 0) { owner { friends { … } } }` the
|
|
207
|
+
`0` does not reach `friends`. A nested field that states a size hands its own
|
|
208
|
+
to the lists directly on it.
|
|
209
|
+
- A connection field that states nothing (no size argument given, none declared
|
|
210
|
+
with a default) hands down nothing: its lists are sized `defaultFirst`, as
|
|
211
|
+
before. A variable, a negative number or an unusable value on it counts as for
|
|
212
|
+
a list: its SDL default, else `defaultFirst`.
|
|
213
|
+
- The rule is structural: any non-list field stating a size sizes the lists
|
|
214
|
+
declaring no size argument directly on its return type, whether or not it is
|
|
215
|
+
a connection. `user(limit: 0) { friends { … } }` sizes `friends` 0. So an
|
|
216
|
+
argument named in `sizeArguments` must size the lists directly under its
|
|
217
|
+
field: when one of yours means something else, leave its name out of
|
|
218
|
+
`sizeArguments` (or rename the argument).
|
|
219
|
+
- Inline fragments, and fragment spreads, on that same selection set pass the
|
|
220
|
+
pending size through. A named fragment is costed once for each distinct
|
|
221
|
+
pending size it is spread under (`...C` under
|
|
222
|
+
`first: 2` and `first: 5` costs differently). Under more than eight distinct
|
|
223
|
+
sizes in one document a fragment's further sizes are rounded up to the next
|
|
224
|
+
power of two, so the work stays linear in the document and the cost only
|
|
225
|
+
over-counts.
|
|
226
|
+
|
|
227
|
+
### Fragments and abstract types
|
|
228
|
+
|
|
229
|
+
Named fragments are followed, each costed once per pending size (see above;
|
|
230
|
+
a fragment's type condition fixes
|
|
231
|
+
its parent type). A fragment spread twice counts twice. Inline fragments and
|
|
232
|
+
fragments on an interface or a union are **summed branch by branch**: a
|
|
233
|
+
selection `... on A { ... } ... on B { ... }` costs A's plus B's, although an
|
|
234
|
+
object is one of them. The cost of a query on an abstract type is overestimated,
|
|
235
|
+
never underestimated.
|
|
236
|
+
|
|
237
|
+
### The refusal
|
|
238
|
+
|
|
239
|
+
```json
|
|
240
|
+
{
|
|
241
|
+
"message": "Operation \"Big\" costs 5002, more than the 1000 allowed",
|
|
242
|
+
"extensions": { "code": "QUERY_TOO_COSTLY", "cost": 5002, "limit": 1000 }
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
`budget` and `defaultFirst` must be integers below `Number.MAX_SAFE_INTEGER`
|
|
247
|
+
(a cost never exceeds it, so a budget at or over it would refuse nothing); the
|
|
248
|
+
rule throws a `RangeError` otherwise.
|
|
249
|
+
|
|
250
|
+
Costs are plain numbers, computed from memoized fragments (no exponential work
|
|
251
|
+
on a hostile query) and held at `Number.MAX_SAFE_INTEGER`: `cost` is exact below
|
|
252
|
+
it and never `Infinity`.
|
|
253
|
+
|
|
254
|
+
## `maxFields({ limit, ignoreIntrospection? })`
|
|
255
|
+
|
|
256
|
+
The size of an operation is the number of field selections in its document,
|
|
257
|
+
fragment spreads expanded. It is not what the executor runs: how often a field
|
|
258
|
+
runs inside a list is `maxListCost`'s business, so `maxFields` alone does not
|
|
259
|
+
bound execution.
|
|
260
|
+
|
|
261
|
+
- Every field node counts 1: aliases count (`a1: id a2: id` is 2), and so does
|
|
262
|
+
`__typename`.
|
|
263
|
+
- A named fragment counts **at each spread**: a fragment of 3 fields spread
|
|
264
|
+
twice adds 6. Each fragment is walked once, so the work stays linear in the
|
|
265
|
+
size of the document even when spreads multiply. A fragment cycle counts 0
|
|
266
|
+
(`NoFragmentCyclesRule` reports it).
|
|
267
|
+
- Inline fragments count their contents. No type condition prunes anything:
|
|
268
|
+
every branch of a union or an interface counts, so the figure can only be
|
|
269
|
+
over the document's field count.
|
|
270
|
+
- `@skip` and `@include` are not evaluated (the rules read the document, not its
|
|
271
|
+
variables): a field they would remove is counted anyway.
|
|
272
|
+
- `__schema` and `__type` and everything under them count like any field.
|
|
273
|
+
The standard introspection query (`getIntrospectionQuery()`) selects **220
|
|
274
|
+
fields** on graphql 16 and 17 (the fragments `FullType`, `InputValue` and
|
|
275
|
+
`TypeRef` are counted at each spread), so a limit under 220 refuses it. Where
|
|
276
|
+
introspection is disabled, or the client is trusted (GraphiQL in development),
|
|
277
|
+
pass `ignoreIntrospection: true`.
|
|
278
|
+
|
|
279
|
+
The refusal:
|
|
280
|
+
|
|
281
|
+
```json
|
|
282
|
+
{
|
|
283
|
+
"message": "Operation \"Wide\" selects 4000 fields, more than the 500 allowed",
|
|
284
|
+
"extensions": { "code": "QUERY_TOO_WIDE", "fields": 4000, "limit": 500 }
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
`fields` is the operation's real count, held at `Number.MAX_SAFE_INTEGER`
|
|
289
|
+
whatever a hostile query multiplies. An anonymous operation reads
|
|
290
|
+
`Operation selects 4000 fields, ...`. `limit` must be an integer from 1 to
|
|
291
|
+
`Number.MAX_SAFE_INTEGER - 1`, else a `RangeError`.
|
|
292
|
+
|
|
293
|
+
### Choosing a limit
|
|
294
|
+
|
|
295
|
+
Count the fields of your largest legitimate query, spreads included (a page
|
|
296
|
+
that spreads a 10-field fragment on 20 list entries reads 200 plus the rest),
|
|
297
|
+
and add headroom: a few hundred covers most screens. Keep the introspection
|
|
298
|
+
query in mind (220) if you leave it counted. `maxFields` complements the other
|
|
299
|
+
two: `maxDepth` bounds nesting, `maxListCost` bounds list multiplication, and
|
|
300
|
+
`maxFields` bounds how many fields one request may name.
|
|
301
|
+
|
|
302
|
+
## Not covered
|
|
303
|
+
|
|
304
|
+
- A size carried inside an input object (`items(page: { first: 1000 })`) is not
|
|
305
|
+
read: the field has no size argument, so it is sized `defaultFirst`. Name the
|
|
306
|
+
size as an argument, or raise `defaultFirst`.
|
|
307
|
+
- A nested list `[[X]]` is sized **once**: the inner lists are not multiplied. A
|
|
308
|
+
schema returning a list of lists should size it with its own argument.
|
|
309
|
+
- The rules read the document, not its variables' values, and not what a
|
|
310
|
+
resolver does: a resolver can still return more than `first` items. They
|
|
311
|
+
complement a page-size cap in the resolver; they do not replace it.
|
package/docs/roadmap.md
CHANGED
|
@@ -18,6 +18,15 @@ Nothing planned yet.
|
|
|
18
18
|
|
|
19
19
|
## Shipped
|
|
20
20
|
|
|
21
|
+
- **Relay connection sizing** — `maxListCost` sizes the `edges` and `nodes` of
|
|
22
|
+
a connection from the connection field's size argument (`items(first: 100)
|
|
23
|
+
{ edges { … } }`), not `defaultFirst`.
|
|
24
|
+
- **Query depth, cost and breadth limits** — `maxDepth`, `maxListCost` and
|
|
25
|
+
`maxFields` are graphql-js validation rules (also for graphql-yoga's
|
|
26
|
+
`addValidationRule`) that refuse a query nested too deep, too costly for a
|
|
27
|
+
schema with list cycles, or selecting too many fields (aliases of a cheap
|
|
28
|
+
field), with the stable codes `QUERY_TOO_DEEP`, `QUERY_TOO_COSTLY` and
|
|
29
|
+
`QUERY_TOO_WIDE`.
|
|
21
30
|
- **Schemas that read the context** — 0.5.0. `validated((context, info) =>
|
|
22
31
|
schema, resolver)` builds the schema on each call from the request's context
|
|
23
32
|
and the field's `info`, for checks that need the request (a uniqueness query
|
package/docs/troubleshooting.md
CHANGED
|
@@ -8,6 +8,7 @@ is the one a client sees.
|
|
|
8
8
|
- [Startup](#startup)
|
|
9
9
|
- [Silent traps](#silent-traps)
|
|
10
10
|
- [Runtime](#runtime)
|
|
11
|
+
- [Query limits](#query-limits)
|
|
11
12
|
- [The bin](#the-bin)
|
|
12
13
|
|
|
13
14
|
## Install
|
|
@@ -405,6 +406,78 @@ every issue. The resolver did not run.
|
|
|
405
406
|
| `Too small: expected number to be >=18` | `min: 18` |
|
|
406
407
|
| `Invalid string: must match pattern /.../` | `pattern` |
|
|
407
408
|
|
|
409
|
+
## Query limits
|
|
410
|
+
|
|
411
|
+
### `Operation "Name" is 9 levels deep, more than the 8 allowed`
|
|
412
|
+
|
|
413
|
+
**When:** an operation is validated with `maxDepth({ depth: 8 })`.
|
|
414
|
+
**Why:** `extensions` is `{ code: 'QUERY_TOO_DEEP', depth: 9, limit: 8 }`. A
|
|
415
|
+
top-level field is depth 1; named fragments are followed, inline fragments add
|
|
416
|
+
nothing, `__typename` does not count. `__schema` and `__type` do: the standard
|
|
417
|
+
introspection query is 15 levels deep (pass `ignoreIntrospection: true` where
|
|
418
|
+
it is trusted). An anonymous operation
|
|
419
|
+
reads `Operation is 9 levels deep, ...`.
|
|
420
|
+
**Fix:** flatten the query, or raise `depth`. See
|
|
421
|
+
[Limiting the depth and cost of a query](guide/query-limits.md).
|
|
422
|
+
|
|
423
|
+
### `Operation "Name" costs 5002, more than the 1000 allowed`
|
|
424
|
+
|
|
425
|
+
**When:** an operation is validated with `maxListCost({ budget: 1000, ... })`.
|
|
426
|
+
**Why:** `extensions` is `{ code: 'QUERY_TOO_COSTLY', cost: 5002, limit: 1000 }`.
|
|
427
|
+
Each field costs 1, times the sizes of the lists enclosing it. A list without a
|
|
428
|
+
usable `first` (a variable, a negative number, nothing) counts for its SDL
|
|
429
|
+
default, else `defaultFirst`, so a query that is cheap with the variable's real
|
|
430
|
+
value can still be refused. A size argument on a field returning an object (a
|
|
431
|
+
connection's `first`) now sizes its `edges` and `nodes`, and any non-list field
|
|
432
|
+
stating a size sizes the lists declaring none directly under it, see
|
|
433
|
+
[Relay connections](guide/query-limits.md#relay-connections).
|
|
434
|
+
**Fix:** ask for fewer items with a literal `first`, or raise `budget`. A query
|
|
435
|
+
on an interface or a union is overestimated (every branch is summed).
|
|
436
|
+
|
|
437
|
+
### `Operation "Name" selects 4000 fields, more than the 500 allowed`
|
|
438
|
+
|
|
439
|
+
**When:** an operation is validated with `maxFields({ limit: 500 })`.
|
|
440
|
+
**Why:** `extensions` is `{ code: 'QUERY_TOO_WIDE', fields: 4000, limit: 500 }`.
|
|
441
|
+
Every field selection counts 1, aliases and `__typename` included, and a named
|
|
442
|
+
fragment counts at each spread; every branch of an inline fragment counts and
|
|
443
|
+
`@skip`/`@include` are not evaluated, so the figure can be over what runs. An
|
|
444
|
+
anonymous operation reads `Operation selects 4000 fields, ...`.
|
|
445
|
+
**Fix:** ask for fewer fields (a fragment spread many times is many fields), or
|
|
446
|
+
raise `limit`. See [Limiting the depth and cost of a query](guide/query-limits.md).
|
|
447
|
+
|
|
448
|
+
### `maxFields: limit must be an integer from 1 to 9007199254740990.`
|
|
449
|
+
|
|
450
|
+
**When:** `maxFields` is called with `0`, a negative, a fractional or a too
|
|
451
|
+
large `limit`.
|
|
452
|
+
**Fix:** pass an integer in range.
|
|
453
|
+
|
|
454
|
+
### `maxDepth: depth must be an integer from 1 to 9007199254740990.`
|
|
455
|
+
|
|
456
|
+
**When:** `maxDepth` is called with `0`, a negative or a fractional `depth`.
|
|
457
|
+
**Fix:** pass an integer in range.
|
|
458
|
+
|
|
459
|
+
### `maxListCost: budget must be an integer from 0 to 9007199254740990.`
|
|
460
|
+
|
|
461
|
+
**When:** `maxListCost` is called with a negative, fractional or too large
|
|
462
|
+
`budget`. A cost never exceeds `Number.MAX_SAFE_INTEGER` (9007199254740991), so a
|
|
463
|
+
budget at or over it would refuse nothing.
|
|
464
|
+
**Fix:** pass an integer in range.
|
|
465
|
+
|
|
466
|
+
### `maxListCost: defaultFirst must be an integer from 0 to 9007199254740990.`
|
|
467
|
+
|
|
468
|
+
**When:** `defaultFirst` is negative, fractional or too large.
|
|
469
|
+
**Fix:** pass the size to assume for a list with no usable size argument.
|
|
470
|
+
|
|
471
|
+
### A query refused although its `first` is small, or an introspection query refused
|
|
472
|
+
|
|
473
|
+
**When:** `QUERY_TOO_COSTLY`, `QUERY_TOO_DEEP` or `QUERY_TOO_WIDE` for GraphiQL's schema fetch or
|
|
474
|
+
a codegen's introspection query.
|
|
475
|
+
**Why:** `__schema` is counted: the standard introspection query is 15 levels
|
|
476
|
+
deep, selects 220 fields and costs on the order of 200,000 on a one-field schema.
|
|
477
|
+
**Fix:** `ignoreIntrospection: true` on the rule where introspection is
|
|
478
|
+
disabled or the client is trusted. A `first: $n` variable counts for the SDL
|
|
479
|
+
default, not for the value sent.
|
|
480
|
+
|
|
408
481
|
## The bin
|
|
409
482
|
|
|
410
483
|
### `typedefs failed: <message>`
|
package/package.json
CHANGED