Lesson 20 / 25

Query Cost, Depth Limits and Persisted Queries

Protecting the server.

Bound what clients can ask for

Because clients compose queries, a single request can be very expensive: deeply nested relationships, huge page sizes or many aliased copies of a field. Protect the server with maximum depth, query complexity/cost analysis (assign costs to fields and multiply by page sizes), maximum page sizes, timeouts and rate limits based on cost. Persisted queries go further: clients send a hash or ID of a pre-registered operation, and the server can reject unknown queries entirely, which suits first-party apps.

An expensive query and the defences

Why limits matter.

# without limits, this asks for up to 100 x 100 x 100 = 1,000,000 nodes
query Expensive {
  users(first: 100) {
    edges { node {
      followers(first: 100) {
        edges { node {
          followers(first: 100) { edges { node { name } } }
        } }
      }
    } }
  }
}

# defences: depth limit (e.g. 7), cost limit (e.g. 1,000 points with list sizes multiplied),
# max first: 50, request timeout, persisted query allow-list for first-party clients

Also limit aliases and batching

Attackers can repeat a field hundreds of times with aliases or send many operations in one batch to bypass simple rate limits.

Quick check: What do persisted queries allow a server to do?

  • Skip the schema entirely
  • Accept only pre-registered operations, identified by hash or ID
  • Store user passwords
  • Disable resolvers
Answer

Accept only pre-registered operations, identified by hash or ID — An allow-list of queries.