2.1 KiB
depth-and-complexity.ts
(no submodules)
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
SelectionSetnesting level per operation. - Counts every
Fieldnode touched, expanding fragment spreads inline once per occurrence. - Throws subclass
GraphqlYogaErrorwithcontext.depth/context.limitfor log-side debugging.
Usage: Basic
import { parse } from "graphql";
import { enforceGuards } from "@suaveplan/api-graphql-yoga";
const doc = parse("{ ping }");
enforceGuards(doc, { maxDepth: 10, maxComplexity: 1000 });
Usage: Advanced
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, returnsvoidon 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.