68 lines
2.1 KiB
Markdown
68 lines
2.1 KiB
Markdown
# `depth-and-complexity.ts`
|
|
|
|
<!-- begin:auto-docs -->
|
|
_(no submodules)_
|
|
<!-- end:auto-docs -->
|
|
|
|
## Purpose
|
|
|
|
Implements the static depth + complexity guard that the Yoga engine runs
|
|
after parsing each query and before execution. Documents that exceed the
|
|
configured limits are rejected with `GRAPHQL_YOGA.DEPTH_EXCEEDED` or
|
|
`GRAPHQL_YOGA.COMPLEXITY_EXCEEDED`, which lets the host reject denial-of-
|
|
service payloads (deeply nested fragments, fan-out spreads) before any
|
|
resolver runs.
|
|
|
|
The guard is intentionally cheap: a single recursive walk over the
|
|
already-parsed `DocumentNode`, no schema awareness, no cost estimator.
|
|
Hosts that want a smarter cost model (database joins, external service
|
|
fan-out) compose a stricter guard on top.
|
|
|
|
## Features
|
|
|
|
- Tracks deepest `SelectionSet` nesting level per operation.
|
|
- Counts every `Field` node touched, expanding fragment spreads inline
|
|
once per occurrence.
|
|
- Throws subclass `GraphqlYogaError` with `context.depth` /
|
|
`context.limit` for log-side debugging.
|
|
|
|
## Usage: Basic
|
|
|
|
```ts
|
|
import { parse } from "graphql";
|
|
import { enforceGuards } from "@suaveplan/api-graphql-yoga";
|
|
|
|
const doc = parse("{ ping }");
|
|
enforceGuards(doc, { maxDepth: 10, maxComplexity: 1000 });
|
|
```
|
|
|
|
## Usage: Advanced
|
|
|
|
```ts
|
|
import { parse } from "graphql";
|
|
import { enforceGuards } from "@suaveplan/api-graphql-yoga";
|
|
|
|
const doc = parse(`
|
|
fragment f on Customer { id name orders { id } }
|
|
query { customer(id: "c1") { ...f } }
|
|
`);
|
|
try {
|
|
enforceGuards(doc, { maxDepth: 3, maxComplexity: 5 });
|
|
} catch (error) {
|
|
// GRAPHQL_YOGA.DEPTH_EXCEEDED or COMPLEXITY_EXCEEDED
|
|
}
|
|
```
|
|
|
|
## API Reference
|
|
|
|
- `enforceGuards(document, { maxDepth, maxComplexity })` — throws on
|
|
violation, returns `void` on pass.
|
|
|
|
## Implementation Notes
|
|
|
|
Fragment cycles would cause unbounded recursion if the walker re-visited
|
|
spreads; in practice the GraphQL `validate()` pass that runs immediately
|
|
after parsing rejects cycle-containing documents, so the depth/complexity
|
|
walker can rely on cycle-free input. Should a future change move the guard
|
|
ahead of validation, add a `Set<string>` of seen spreads to short-circuit.
|