{"id":"317a98ca35e7d88a","repo":"nestjs/nest","slug":"query-is-too-complex-complexity-maximum-allow","errorCode":null,"errorMessage":"Query is too complex: ${complexity}. Maximum allowed complexity: 20","messagePattern":"Query is too complex: (.+?)\\. Maximum allowed complexity: 20","errorType":"exception","errorClass":"GraphQLError","httpStatus":null,"severity":"warning","filePath":"sample/23-graphql-code-first/src/common/plugins/complexity.plugin.ts","lineNumber":31,"sourceCode":"  constructor(private gqlSchemaHost: GraphQLSchemaHost) {}\n\n  async requestDidStart(): Promise<GraphQLRequestListener<any>> {\n    const { schema } = this.gqlSchemaHost;\n\n    return {\n      async didResolveOperation({ request, document }) {\n        const complexity = getComplexity({\n          schema,\n          operationName: request.operationName,\n          query: document,\n          variables: request.variables,\n          estimators: [\n            fieldExtensionsEstimator(),\n            simpleEstimator({ defaultComplexity: 1 }),\n          ],\n        });\n        if (complexity >= 20) {\n          throw new GraphQLError(\n            `Query is too complex: ${complexity}. Maximum allowed complexity: 20`,\n          );\n        }\n        console.log('Query Complexity:', complexity);\n      },\n    };\n  }\n}\n","sourceCodeStart":13,"sourceCodeEnd":40,"githubUrl":"https://github.com/nestjs/nest/blob/6ec0e2783d15290732447f304d8549b591b9749e/sample/23-graphql-code-first/src/common/plugins/complexity.plugin.ts#L13-L40","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nif (complexity >= 20) {\n  throw new GraphQLError(`Query is too complex: ${complexity}. Maximum allowed complexity: 20`);\n}\n\n// after\nconst MAX = Number(process.env.GQL_MAX_COMPLEXITY ?? 100);\nif (complexity > MAX) {\n  throw new GraphQLError(`Query is too complex: ${complexity}. Maximum allowed complexity: ${MAX}`);\n}","handlingStrategy":"validation","validationCode":"// Client-side: estimate complexity before sending, or introspect the schema cap.\n// Crude heuristic: count selected fields; if it exceeds your budget, narrow the query.\nconst fieldCount = countSelectionFields(QUERY_STRING);\nif (fieldCount > CLIENT_COMPLEXITY_BUDGET) {\n  throw new Error(`Query too expensive (${fieldCount} fields) — narrow the selection`);\n}\n\n// Server-side: make the cap env-driven instead of hard-coding 20.\nconst MAX = Number(process.env.GQL_MAX_COMPLEXITY ?? 100);","typeGuard":"import { GraphQLError } from 'graphql';\n\nfunction isComplexityError(e: unknown): boolean {\n  return e instanceof GraphQLError\n    && /too complex/i.test(e.message);\n}","tryCatchPattern":"// In an Apollo Client / urql error link\nonError(({ graphQLErrors }) => {\n  graphQLErrors?.forEach(err => {\n    if (/too complex/i.test(err.message)) {\n      // tell the user to narrow the query, or auto-refetch a trimmed operation\n    }\n  });\n});","preventionTips":["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."],"tags":["graphql","apollo","complexity","dos-protection","nestjs"],"analyzedSha":"6ec0e2783d15290732447f304d8549b591b9749e","analyzedAt":"2026-08-03T17:42:23.673Z","schemaVersion":2}