Card 07/ 09
GraphQL Queries With No Limit on Their Cost
A query nested five levels deep can ask the server for millions of objects in one request.
In GraphQL the client decides the shape of the response, including how deep it nests and how many items each list returns. A server that resolves any valid query lets one request ask for millions of objects, and the database pays for every one. Without limits, a single query copied from a forum can take a public endpoint down.
The symptom. A public GraphQL endpoint slows to a halt. The logs show one client and one request, a query that follows a social graph three levels deep.
Input{ user(id: 1) { friends(first: 100) { friends(first: 100) { friends(first: 100) { name } } } } }
Level 1
- 100 friends100
One list of up to 100.
Level 2
- 100 × 100 friends10,000
Each of the 100 has its own list of up to 100.
Level 3
- 100 × 100 × 100 friends1,000,000
Each of those 10,000 has up to 100 more, each with a name to load.
ResultOne request of under a hundred characters asks for up to a million objects.
Why it happens. The flexibility that lets a screen ask for exactly what it needs also lets any client ask for everything. Fetching only the fields asked for does not help when what was asked for is enormous.
What it costs. Database load, memory and time multiplied by every level, and an outage for every other client.
What to do.
- Limit query depth, for example to five levels.
- Calculate a cost for each query before running it, from the list sizes it asks for, and refuse any over a budget.
- Require a
firstorlastargument with a maximum on every list, so no list is unbounded. - For a known set of clients, accept only persisted queries, registered in advance, and refuse anything else.
- Rate limit by query cost as well as by request count.
← → move through sections and cards