DEV Community

Mateo Díaz López
Mateo Díaz López

Posted on

Limiting GraphQL Query Depth the Right Way

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
            }
          }
        }
      }
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

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:

  1. The library hooks into GraphQL’s validation phase
  2. It traverses the query AST
  3. It calculates depth based on nested field selections
  4. It tracks the maximum execution depth
  5. If the depth exceeds maxDepth, the query is rejected before execution

Depth calculation example

✅ Valid query (depth = 3)

query {
  user {
    profile {
      name
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

❌ Invalid query (depth = 4)

query {
  user {
    profile {
      address {
        city
      }
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

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 }),
  ],
});

Enter fullscreen mode Exit fullscreen mode

Apollo Server (NestJS)

import { createDepthLimitRule } from "graphql-safe-depth";

GraphQLModule.forRoot({
  autoSchemaFile: true,
  validationRules: [
    createDepthLimitRule({ maxDepth: 3 }),
  ],
});

Enter fullscreen mode Exit fullscreen mode

⚙️ Configuration options

createDepthLimitRule({
  maxDepth: number;
  ignoreIntrospection?: boolean;
  message?: (depth: number, maxDepth: number) => string;
});

Enter fullscreen mode Exit fullscreen mode

maxDepth (required)

The maximum allowed execution depth.
reateDepthLimitRule({ maxDepth: 3 });

ignoreIntrospection (default: true)

Safely ignores GraphQL introspection fields.

createDepthLimitRule({
  maxDepth: 3,
  ignoreIntrospection: false,
});
Enter fullscreen mode Exit fullscreen mode

message (optional)

Customize the validation error message.

createDepthLimitRule({
  maxDepth: 3,
  message: (depth, max) =>
    `Query depth ${depth} exceeds the allowed maximum of ${max}`,
});

Enter fullscreen mode Exit fullscreen mode

🔐 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)