GraphQL Query Depth Limiting: Configuration and Testing

June 5, 2026 | 11 min read | For API Engineers

A GraphQL server without depth and complexity limits is an amplification attack waiting to happen. A single deeply nested query can trigger thousands of database resolver calls because GraphQL resolves each field independently. The attacker writes a query once; the server does the multiplication. This post covers the specific configuration for the three most common GraphQL server implementations -- Apollo Server, graphql-go, and Strawberry Python -- and provides test queries to verify the limits are working.

Why Depth Limits Alone Are Insufficient

Depth limits prevent unbounded nesting, but they do not prevent breadth amplification. A query with depth 5 but 100 sibling fields at each level can still trigger 100^5 = 10 billion resolver invocations in the worst case. Complexity limits are required in addition to depth limits. Complexity assigns a cost to each field and rejects queries that exceed a total cost threshold. The combination of depth + complexity + per-query timeout provides adequate protection.

Apollo Server Configuration

// Apollo Server v4 with depth and complexity limits
import { ApolloServer } from '@apollo/server';
import depthLimit from 'graphql-depth-limit';
import { createComplexityLimitRule } from 'graphql-validation-complexity';

const server = new ApolloServer({
  typeDefs,
  resolvers,
  validationRules: [
    // Reject queries deeper than 5 levels
    depthLimit(5, { ignore: ['__schema', '__type'] }),

    // Reject queries with complexity above 1000
    // Default cost per field: 1
    // List field multiplier: configured per-field
    createComplexityLimitRule(1000, {
      onCost: (cost) => {
        console.log('Query complexity:', cost);
      },
      formatErrorMessage: (cost) =>
        `Query complexity ${cost} exceeds limit of 1000`
    })
  ],
  // Introspection disabled in production
  introspection: process.env.NODE_ENV !== 'production'
});

graphql-go Configuration

// graphql-go with depth limit middleware
package main

import (
    "github.com/graph-gophers/graphql-go"
    "github.com/graph-gophers/graphql-go/relay"
)

func main() {
    opts := []graphql.SchemaOpt{
        graphql.MaxDepth(5),
        graphql.MaxParallelism(10), // Limit concurrent resolver goroutines
    }

    schema := graphql.MustParseSchema(schemaString, &resolver{}, opts...)

    http.Handle("/query", &relay.Handler{Schema: schema})
}

Strawberry Python Configuration

# Strawberry Python with depth and complexity limits
import strawberry
from strawberry.extensions import QueryDepthLimiter, AddValidationRules
from graphql import GraphQLError

def check_query_complexity(complexity):
    if complexity > 1000:
        raise GraphQLError(f"Query complexity {complexity} exceeds limit of 1000")

schema = strawberry.Schema(
    query=Query,
    extensions=[
        QueryDepthLimiter(max_depth=5),
    ]
)

# FastAPI integration with timeout
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter

app = FastAPI()
graphql_app = GraphQLRouter(
    schema,
    # Request timeout prevents long-running queries
    execution_context_class=TimedExecutionContext  # custom class
)

Test Queries for Verifying Depth Limits

# Test query that should be REJECTED by depth limit (depth = 7)
query DepthTest {
  user(id: "1") {          # depth 1
    friends {              # depth 2
      friends {            # depth 3
        posts {            # depth 4
          comments {       # depth 5
            author {       # depth 6
              username     # depth 7 - SHOULD REJECT
            }
          }
        }
      }
    }
  }
}

# Expected response:
# {"errors": [{"message": "Syntax Error: Field 'username' exceeds maximum operation depth of 5"}]}

Testing from DDactic

DDactic's GraphQL assessment sends a graduated series of depth-increasing queries and records whether each returns an error at the expected depth. It also tests complexity limits with width-increasing queries (many fields at moderate depth) and verifies that introspection is disabled in production. A passing result confirms that the server rejects depth-7 queries, complexity-1000+ queries, and introspection queries.

Test Your GraphQL Depth Limits

DDactic's scanner probes your GraphQL endpoints with graduated depth and complexity queries to verify limits are enforced.

Run a Free Scan
GraphQLQuery DepthComplexity LimitsApollo ServerDDoS Protection