Join us for GraphQL Summit, October 10-12 in San Diego. Use promo code ODYSSEY for $400 off your pass.
Launch GraphOS Studio

Enforcing operation limits in the Apollo Router

With GraphOS Enterprise

⚠️ This is an Enterprise feature of the Apollo Router. It requires an organization with a GraphOS Enterprise plan.

If your organization doesn't currently have an Enterprise plan, you can test out this functionality by signing up for a free Enterprise trial.

You can define operation limits in your 's configuration to reject potentially malicious requests. An that exceeds any specified limit is rejected (unless you run your in warn_only mode).


To use limits, you must run v1.17 or later of the Apollo . Download the latest version.

You define limits in your 's YAML config file, like so:

max_depth: 100
max_height: 200
max_aliases: 30
max_root_fields: 20
# Uncomment to enable warn_only mode
# warn_only: true

Each limit takes an integer value. You can define any combination of supported limits.

Supported limits


Limits the deepest nesting of selection sets in an , including s in s.

The GetBook below has depth three:

query GetBook {
book { # Depth 1 (root field)
fragment bookDetails on Book {
details { # Depth 2 (nested under `book`)
... on ProductDetailsBook {
country # Depth 3 (nested under `details`)


Limits the number of unique s included in an , including fields of s. If a particular field is included multiple times via es, it's counted only once.

The GetUser below has height three:

query GetUser {
user { # 1
id # 2
name # 3
username: name # Aliased duplicate (not counted)

Each unique increments an 's height by one, regardless of that field's return type (, object, or list).


Limits the total number of ed s in an , including fields of s.

The GetUser below includes three es:

query GetUser {
user {
nickname: name # 1
username: name # 2
handle: name # 3

Each ed increments the alias count by one, regardless of that field's return type (, object, or list).


Limits the number of root s in an , including root fields in s. If a particular root field is included multiple times via es, each usage is counted.

The following includes three root s:

query GetTopProducts {
topBooks { # 1
topMovies { # 2
topGames { # 3

warn_only mode

If you run your in warn_only mode, s that exceed defined limits are not rejected. Instead, the processes these s as usual and emits a WARN trace that notes all exceeded limits, like so:

2023-03-15T19:08:23.123456Z WARN apollo_router::operation_limits: max_depth exceeded, max_depth: 3, current_op_depth: 5, operation: "query GetOwnerLocation {cat {owner {location {postalCode}}}}"

Running in warn_only mode can be useful while you're testing to determine the most appropriate limits to set for your .

You can enable or disable warn_only mode in your 's YAML config file, like so:

warn_only: true # warn_only mode always enabled

Response format for exceeded limits

Whenever your rejects a request because it exceeds an limit, the router responds with a 400 HTTP status code and a standard GraphQL error response body:

# HTTP 400
"data": {},
"errors": [
"message": "Maximum height (field count) limit exceeded in this operation",
"extensions": {

If you run your in warn_only mode, the logs the limit violation but executes the as normal, returning a 200 status code with the expected response.

Subgraph Authentication
Safelisting with persisted queries
Edit on GitHubEditForumsDiscord