Query Cost Analysis and Depth Limiting
Protect the API from abusive queries using depth limits, complexity scoring, and persisted queries.
Why GraphQL Needs Query Guards
REST endpoints have a fixed cost: you call /users/1/posts and the server decides how much work that is. GraphQL flips control to the client. A single endpoint accepts arbitrary nested queries, so a malicious or careless client can ask for something enormous.
Consider a query that walks user -> friends -> friends -> friends .... Each level multiplies the number of resolvers fired. A few hundred bytes of query text can trigger millions of database round-trips.
- Depth limiting caps how deeply nested a query can be.
- Complexity scoring caps the estimated total cost.
- Persisted queries only allow pre-approved query documents.
In this lesson we build all three for a Strawberry + FastAPI service.
The Nested-Query Attack
The classic abuse is a deeply nested, self-referential query. If your schema lets Author resolve posts and Post resolve author, a client can ping-pong between them indefinitely.
Below is the shape of such a query as plain text. Even without running it, notice that every extra posts { author { ... } } level can multiply fan-out at the data layer.
abusive_query = """
query {
author(id: 1) {
posts {
author {
posts {
author {
posts { title }
}
}
}
}
}
}
"""
# A simple depth counter on the raw text gives a rough idea.
depth = abusive_query.count("{")
print(f"Approximate nesting braces: {depth}")All lessons in this course
- Defining Types, Queries and Mutations
- Solving N+1 Queries with DataLoaders
- Real-Time GraphQL Subscriptions
- Query Cost Analysis and Depth Limiting