0
0
GraphqlHow-ToBeginner · 4 min read

How to Limit Query Complexity in GraphQL for Better Performance

To limit query complexity in GraphQL, use middleware or libraries like graphql-query-complexity to analyze and restrict queries based on their depth or cost before execution. This prevents expensive queries from overloading your server by setting a maximum allowed complexity or depth.
📐

Syntax

Limiting query complexity typically involves adding a validation rule or middleware that inspects incoming queries. Key parts include:

  • maxComplexity: The maximum allowed complexity score for a query.
  • complexityEstimator: A function to calculate complexity per field.
  • validationRules: GraphQL server option to add complexity checks.
javascript
const { getComplexity, simpleEstimator } = require('graphql-query-complexity');

const complexityRule = (maxComplexity) => ({
  validationRules: [
    (context) => {
      const complexity = getComplexity({
        schema: context.schema,
        query: context.document,
        estimators: [simpleEstimator({ defaultComplexity: 1 })],
      });
      if (complexity > maxComplexity) {
        throw new Error(`Query is too complex: ${complexity}. Maximum allowed is ${maxComplexity}.`);
      }
      return null;
    },
  ],
});
💻

Example

This example shows how to use the graphql-query-complexity library with an Apollo Server to limit queries to a maximum complexity of 100.

javascript
const { ApolloServer, gql } = require('apollo-server');
const { getComplexity, simpleEstimator } = require('graphql-query-complexity');

const typeDefs = gql`
  type Query {
    books: [Book]
  }
  type Book {
    title: String
    author: String
  }
`;

const resolvers = {
  Query: {
    books: () => [
      { title: 'Book 1', author: 'Author A' },
      { title: 'Book 2', author: 'Author B' },
    ],
  },
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
  validationRules: [
    (context) => {
      const complexity = getComplexity({
        schema: context.schema,
        query: context.document,
        estimators: [simpleEstimator({ defaultComplexity: 1 })],
      });
      if (complexity > 100) {
        throw new Error(`Query is too complex: ${complexity}. Maximum allowed is 100.`);
      }
      return null;
    },
  ],
});

server.listen().then(({ url }) => {
  console.log(`Server ready at ${url}`);
});
Output
Server ready at http://localhost:4000/
⚠️

Common Pitfalls

Common mistakes when limiting query complexity include:

  • Not setting a complexity limit, which leaves the server vulnerable to expensive queries.
  • Using only query depth limiting, which may not capture all costly queries.
  • Failing to update complexity estimators when schema changes, causing inaccurate complexity calculations.
  • Throwing generic errors without clear messages, making debugging harder.
javascript
/* Wrong: No complexity check, server vulnerable */
const server = new ApolloServer({ typeDefs, resolvers });

/* Right: Add complexity validation rule */
const serverWithLimit = new ApolloServer({
  typeDefs,
  resolvers,
  validationRules: [complexityRule(100).validationRules[0]],
});
📊

Quick Reference

ConceptDescription
maxComplexityMaximum allowed complexity score for queries
complexityEstimatorFunction to assign cost to each field in a query
validationRulesGraphQL server option to add query complexity checks
graphql-query-complexityPopular library to calculate and limit query complexity
Depth LimitingAlternative method to limit query depth instead of complexity

Key Takeaways

Use libraries like graphql-query-complexity to measure and limit query cost.
Set a reasonable maxComplexity to protect your server from expensive queries.
Combine complexity limiting with depth limiting for better protection.
Update complexity estimators when your schema changes to keep limits accurate.
Provide clear error messages when rejecting queries due to complexity.