Introducing graphql-safe-depth
GraphQL is powerful, flexible, and expressive — but that flexibility can also become a liability if queries are not properly constrained.
One common attack vector (or accidental performance issue) is overly deep queries that cause excessive resolver execution, memory usage, or even denial-of-service scenarios.
In this post, I’ll explain:
- 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 in Apollo Server and NestJS
- When and how to combine it with other security measures
🚨 The problem: Deep GraphQL queries
Consider the following query:
query {
user {
posts {
comments {
author {
profile {
avatar {
url
}
}
}
}
}
}
}
At first glance, this looks harmless.
But under the hood, this can:
Trigger many resolver executions
- Cause 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.
That responsibility belongs to the server.
🤔 Why existing solutions fall short
There are existing GraphQL depth-limit libraries, but many of them have issues such as:
❌Counting fields instead of execution depth
Some libraries count the total number of fields rather than the deepest execution path, which leads to false positives or confusing behavior.
❌ Breaking introspection
GraphQL introspection queries (__schema, __type, __typename) are often deep by nature.
Blocking or miscounting them breaks tools like GraphQL Playground or Apollo Studio.
❌ Hard to reason about
Some implementations are difficult to customize, debug, or explain to a team.
I wanted something simpler, predictable, and easy to trust.
✅ The approach: graphql-safe-depth
graphql-safe-depth is a lightweight GraphQL validation rule focused on one thing:
Limiting real execution depth — not field count.
Design goals
- 🧠 Measure the deepest resolver path
- 🔍 Ignore introspection fields by default
- 🧩 Fully support fragments
- ⚡ Zero runtime dependencies
- 🛠 TypeScript-first, JavaScript-friendly
🔧 How graphql-safe-depth works
At a high level:
- 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;
ignoreIntrospection?: boolean;
message?: (depth: number, maxDepth: number) => string;
});
maxDepth (required)
The maximum allowed execution depth.
reateDepthLimitRule({ maxDepth: 3 });
ignoreIntrospection (default: true)
Safely ignores GraphQL introspection fields.
createDepthLimitRule({
maxDepth: 3,
ignoreIntrospection: false,
});
message (optional)
Customize the 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.
For production GraphQL APIs, it should be combined 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
or
yarn add graphql-safe-depth
🔗 Links
GitHub repository
👉 [https://github.com/Mateodiaz401/graphql-safe-depth]
npm package
👉 [https://www.npmjs.com/package/graphql-safe-depth]
🧠 Final thoughts
This library started as a learning exercise and evolved into a production-ready tool with a stable v1.0.0 release.
If you’re running GraphQL in production and want a simple, predictable depth limit, I hope graphql-safe-depth helps.
Feedback, issues, and contributions are very welcome 🙌
Top comments (0)