Limiting GraphQL Query Depth the Right Way
Source: Dev.to
Introducing graphql‑safe‑depth
GraphQL is powerful, flexible, and expressive — but that flexibility can become a liability if queries are not properly constrained.
Overly deep queries can cause excessive resolver execution, high memory usage, or even denial‑of‑service scenarios.
In this post you’ll learn:
- Why deep GraphQL queries are a real problem
- Why many existing depth‑limit solutions fall short
- How graphql‑safe‑depth approaches the problem differently
- How to use it with Apollo Server and NestJS
- When and how to combine it with other security measures
🚨 The problem: Deep GraphQL queries
query {
user {
posts {
comments {
author {
profile {
avatar {
url
}
}
}
}
}
}
}
At first glance this looks harmless, but it can:
- Trigger N+1 query explosions
- Consume significant CPU and memory
- Become a DoS vector, intentionally or not
GraphQL does not impose any default depth or complexity limits.
🤔 Why existing solutions fall short
| Issue | Description |
|---|---|
| ❌ Counting fields instead of execution depth | Some libraries count the total number of fields rather than the deepest execution path, leading to false positives or confusing behavior. |
| ❌ Breaking introspection | Introspection queries (__schema, __type, __typename) are often deep by nature and should not be blocked. |
| ❌ Hard to reason about | Certain implementations are difficult to customize, debug, or explain to a team. |
✅ The approach: graphql‑safe‑depth
graphql‑safe‑depth is a lightweight GraphQL validation rule focused on one thing:
- 🧠 Measure the deepest resolver path
- 🔍 Ignore introspection fields by default
- 🧩 Fully support fragments
- ⚡ Zero runtime dependencies
- 🛠 TypeScript‑first, JavaScript‑friendly
How it works
- The library hooks into GraphQL’s validation phase.
- It traverses the query AST.
- It calculates depth based on nested field selections.
- It tracks the maximum execution depth.
- If the depth exceeds
maxDepth, the query is rejected before execution.
Depth calculation example
✅ Valid query (depth = 3)
query {
user {
profile {
name
}
}
}
❌ Invalid query (depth = 4)
query {
user {
profile {
address {
city
}
}
}
}
Only the deepest execution path matters — not the total number of fields.
🚀 Usage examples
Apollo Server (Node.js)
import { ApolloServer } from "apollo-server";
import { createDepthLimitRule } from "graphql-safe-depth";
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [
createDepthLimitRule({ maxDepth: 3 }),
],
});
Apollo Server (NestJS)
import { createDepthLimitRule } from "graphql-safe-depth";
GraphQLModule.forRoot({
autoSchemaFile: true,
validationRules: [
createDepthLimitRule({ maxDepth: 3 }),
],
});
⚙️ Configuration options
createDepthLimitRule({
maxDepth: number; // required
ignoreIntrospection?: boolean; // default: true
message?: (depth: number, maxDepth: number) => string; // optional
});
-
maxDepth – The maximum allowed execution depth.
createDepthLimitRule({ maxDepth: 3 }); -
ignoreIntrospection – When
true, introspection fields are ignored.createDepthLimitRule({ maxDepth: 3, ignoreIntrospection: false, }); -
message – Custom validation error message.
createDepthLimitRule({ maxDepth: 3, message: (depth, max) => `Query depth ${depth} exceeds the allowed maximum of ${max}`, });
🔐 Security considerations
Depth limiting is not a silver bullet, but it works well together with:
- ✅ Query complexity limits
- ✅ Proper authentication & authorization
- ✅ Rate limiting
- ✅ Caching and batching (e.g., DataLoader)
graphql‑safe‑depth focuses on doing one thing well — preventing dangerously deep queries in a predictable way.
📦 Installation
npm i graphql-safe-depth
🔗 Links
- GitHub repository:
- npm package:
🧠 Final thoughts
The library started as a learning exercise and has evolved into a production‑ready tool with a stable v1.0.0 release. If you’re running GraphQL in production and need a simple, predictable depth limit, graphql‑safe‑depth may be a good fit.
Feedback, issues, and contributions are very welcome! 🙌