Limit GraphQL Query Complexity
Compute a cost for each query from field weights and list multipliers, reject queries above a budget, and return a clear error before execution.
Task
Protect a GraphQL server from deeply nested or wide queries that would exhaust the database.
When to use this
A single malicious or careless query with deep nesting and large list multipliers ties up the server, and depth limiting alone does not catch wide fan-out.
Prerequisites
- A GraphQL server with validation hooks
- A cost model per field
Steps
1. Assign a cost to scalar fields and multipliers to list fields.
Expected output: Each field contributes predictable cost.
2. Compute total complexity before execution and reject over budget.
Expected output: Expensive queries never reach resolvers.
3. Return a structured error naming the limit and the computed cost.
curl -X POST https://api.example.com/graphql -d '{"query":"{ user { posts { comments { author } } } }"}'Expected output: Clients can fix their query.
4. Consider pagination everywhere so lists have bounded multipliers.
Expected output: The cost model has finite worst cases.
Troubleshooting
Problem
Legitimate queries rejected
Solution
Tune weights from real query profiles and allow a higher budget for authenticated clients.
Problem
Complexity ignores runtime cost
Solution
Combine static complexity with a per-resolver timeout so a cheap-looking but slow resolver is still bounded.
Limitations
- Static complexity is an approximation and can both over- and under-estimate real cost.
- It must be tuned against production traffic or it will either block valid queries or miss abuse.
Context
Protect a GraphQL server from deeply nested or wide queries that would exhaust the database. A single malicious or careless query with deep nesting and large list multipliers ties up the server, and depth limiting alone does not catch wide fan-out.
Steps
- Assign a cost to scalar fields and multipliers to list fields. Expected: Each field contributes predictable cost.
- Compute total complexity before execution and reject over budget. Expected: Expensive queries never reach resolvers.
- Return a structured error naming the limit and the computed cost. Expected: Clients can fix their query.
- Consider pagination everywhere so lists have bounded multipliers. Expected: The cost model has finite worst cases.
const cost = calculateComplexity(query, fieldWeights);
if (cost > BUDGET) {
throw new GraphQLError(`Query too complex: ${cost} > ${BUDGET}`);
}
Verify
- Each field contributes predictable cost.
- Expensive queries never reach resolvers.
- Clients can fix their query.
- The cost model has finite worst cases.
Limits
- Static complexity is an approximation and can both over- and under-estimate real cost.
- It must be tuned against production traffic or it will either block valid queries or miss abuse.