nestjs/nest · warning · GraphQLError
Query is too complex: ${complexity}. Maximum allowed complex
Error message
Query is too complex: ${complexity}. Maximum allowed complexity: 20 What it means
A graphql-query-complexity guard implemented as a NestJS Apollo @Plugin (ComplexityPlugin). For every resolved operation it runs two estimators — fieldExtensionsEstimator() then simpleEstimator({ defaultComplexity: 1 }) — to score the query, and at sample/23-graphql-code-first/src/common/plugins/complexity.plugin.ts:31 it throws a GraphQLError when the score reaches 20. The plugin executes in didResolveOperation, i.e. BEFORE the resolvers run, so a rejected query never touches the data layer. Its purpose is DoS protection against catastrophically nested or wide queries.
Source
Thrown at sample/23-graphql-code-first/src/common/plugins/complexity.plugin.ts:31
constructor(private gqlSchemaHost: GraphQLSchemaHost) {}
async requestDidStart(): Promise<GraphQLRequestListener<any>> {
const { schema } = this.gqlSchemaHost;
return {
async didResolveOperation({ request, document }) {
const complexity = getComplexity({
schema,
operationName: request.operationName,
query: document,
variables: request.variables,
estimators: [
fieldExtensionsEstimator(),
simpleEstimator({ defaultComplexity: 1 }),
],
});
if (complexity >= 20) {
throw new GraphQLError(
`Query is too complex: ${complexity}. Maximum allowed complexity: 20`,
);
}
console.log('Query Complexity:', complexity);
},
};
}
}
View on GitHub (pinned to 6ec0e2783d)
Solutions
- Set complexity directives (@complexity) on expensive/list fields via fieldExtensionsEstimator so wide lists cost proportionally and the threshold becomes meaningful.
- Raise the threshold (make it configurable via env: process.env.GQL_MAX_COMPLEXITY) rather than hard-coding 20.
- On the client, reduce the selection set — fetch only the fields you render, paginate list queries, and avoid deep nesting.
- If you intentionally want to disable the guard locally, gate the plugin behind an env flag or remove it from the module's provider list.
Example fix
// before
if (complexity >= 20) {
throw new GraphQLError(`Query is too complex: ${complexity}. Maximum allowed complexity: 20`);
}
// after
const MAX = Number(process.env.GQL_MAX_COMPLEXITY ?? 100);
if (complexity > MAX) {
throw new GraphQLError(`Query is too complex: ${complexity}. Maximum allowed complexity: ${MAX}`);
} Defensive patterns
Strategy: validation
Validate before calling
// Client-side: estimate complexity before sending, or introspect the schema cap.
// Crude heuristic: count selected fields; if it exceeds your budget, narrow the query.
const fieldCount = countSelectionFields(QUERY_STRING);
if (fieldCount > CLIENT_COMPLEXITY_BUDGET) {
throw new Error(`Query too expensive (${fieldCount} fields) — narrow the selection`);
}
// Server-side: make the cap env-driven instead of hard-coding 20.
const MAX = Number(process.env.GQL_MAX_COMPLEXITY ?? 100); Type guard
import { GraphQLError } from 'graphql';
function isComplexityError(e: unknown): boolean {
return e instanceof GraphQLError
&& /too complex/i.test(e.message);
} Try / catch
// In an Apollo Client / urql error link
onError(({ graphQLErrors }) => {
graphQLErrors?.forEach(err => {
if (/too complex/i.test(err.message)) {
// tell the user to narrow the query, or auto-refetch a trimmed operation
}
});
}); Prevention
- Configure fieldExtensionsEstimator with per-field @complexity directives so list/relation fields cost proportionally.
- Expose the threshold via env so you can tune per environment without a code change.
- Prefer persisted queries / allow-lists in production so clients cannot submit arbitrary expensive operations.
- On the client, request only the fields you render and paginate lists.
When it happens
Trigger: Any GraphQL operation whose summed field cost is >= 20: e.g., querying recipes() (a list) and selecting enough fields/relations that simpleEstimator's default cost of 1 per field adds up to 20, or a deeply nested selection set where fieldExtensionsEstimator has no per-field cost defined so everything falls back to 1.
Common situations: The threshold of 20 is intentionally low for the sample and trips on real-world list queries; fieldExtensionsEstimator is not configured with per-field complexity/depth multipliers, so list fields costing the same as scalars blow the budget fast; a client requests all recipes with full nesting; a React/Apollo client over-fetches after adding a new field.
Related errors
AI-assisted analysis of nestjs/nest@6ec0e2783d (2026-08-03).
Data as JSON: /data/errors/317a98ca35e7d88a.json.
Report an issue: GitHub.