Agent Wranglers

API-96Required

Limit the depth and cost of every GraphQL query

Before a GraphQL query runs, measure its depth, its aliases and its estimated cost, and refuse it above fixed caps. Start with depth 10, 15 aliases and a cost of 1,000.

How to do it

Skip this rule when the server runs only known queries from your own apps. An open endpoint lets one short query nest lists inside lists, so 10 levels of 10 friends ask for 10 billion records. Give each field a weight, and multiply list fields by their first or limit argument. Add the weights and compare the total with the cap during validation, before any resolver runs. Also cap the operations in one batch and the total time of one request. Name the caps in the schema docs, and return the cost in the extensions of each answer, so clients can stay below it. One request can hold hundreds of operations through aliases or a batch. Count each login, code check or other costly field as its own attempt inside the resolver. In production, turn off introspection and field suggestions unless the API is public. Check ownership in the resolver of every object, as check ownership on every record says, including objects reached through node and nested fields.

Examples

JavaScript example

import { createComplexityRule, simpleEstimator, fieldExtensionsEstimator } from 'graphql-query-complexity';
import depthLimit from 'graphql-depth-limit';

const MAX_DEPTH = 10;
const MAX_COST = 1000;

export const validationRules = [
  depthLimit(MAX_DEPTH),
  createComplexityRule({
    maximumComplexity: MAX_COST,
    estimators: [fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 })],
  }),
];

Python example

from strawberry.extensions import MaxAliasesLimiter, QueryDepthLimiter

MAX_DEPTH = 10
MAX_ALIASES = 15

schema = strawberry.Schema(
    query=Query,
    extensions=[QueryDepthLimiter(max_depth=MAX_DEPTH), MaxAliasesLimiter(max_alias_count=MAX_ALIASES)],
)

How to check

Send a query nested 11 levels deep, one with 16 aliases and one whose first arguments multiply past the cost cap. Each is refused before any resolver runs, with an error code such as QUERY_TOO_COMPLEX, and the database log shows no query for it. In production, { __schema { types { name } } } and a misspelled field return no type names or suggestions. One request with 50 aliased logins for a test account is limited like 50 separate logins. As test user A, a request for user B's record through a nested field or node(id:) returns no data.

Sources

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/api-design/graphql-and-grpc/graphql-cost-limits">Agent Wranglers API design playbook</a>. In code you write, add a comment next to it with the same link.