GraphQL Chained Resolvers
Chained resolvers describe how GraphQL passes the output of one resolver as the input to the next. This chain runs from the root query down through every nested field the client requested. Understanding the chain helps you know exactly what data is available at each step and where to make your database calls.
The Chain in Action
Query:
───────
{
store {
name
manager {
name
email
}
products {
title
price
}
}
}
Resolver execution order:
──────────────────────────
1. Query.store
→ Returns: { id: "s1", name: "Tech Hub", managerId: "m7" }
2. Store.name (parent = store object)
→ Default resolver → returns "Tech Hub"
3. Store.manager (parent = store object)
→ Runs: db.findManager(parent.managerId)
→ Returns: { id: "m7", name: "Anand", email: "anand@x.com" }
4. Manager.name (parent = manager object)
→ Default resolver → returns "Anand"
5. Manager.email (parent = manager object)
→ Default resolver → returns "anand@x.com"
6. Store.products (parent = store object)
→ Runs: db.findProducts(parent.id)
→ Returns: [{ id: "p1", title: "Laptop", price: 899 }, ...]
7. Product.title, Product.price (parent = each product)
→ Default resolvers → return title and price from each object
What "parent" Contains at Each Level
Level parent is ───── ────────── Query.* undefined (nothing above) Store.* the object returned by Query.store Manager.* the object returned by Store.manager Product.* the object returned by one item in Store.products
Chaining Allows Lazy Loading
The products resolver only runs if the client actually asked for products. If the query omits products, that resolver never executes and no database call is made for it. This is "lazy" — work happens only on demand.
Query A (requests products): Query B (omits products):
───────────────────────────── ────────────────────────
{ store { name products { { store { name } }
title } } }
Resolvers that run: Resolvers that run:
Query.store ✓ Query.store ✓
Store.name ✓ Store.name ✓
Store.products ✓ Store.products ✗ (skipped)
Product.title ✓ (no DB call for products)
Resolver Chain with a List
When a resolver returns a list, GraphQL runs the child resolvers once for each item in the list. If Store.products returns 10 products, the Product.title resolver runs 10 times — once per product.
Store.products → [product1, product2, product3] Product.title runs for product1 → "Laptop" Product.title runs for product2 → "Mouse" Product.title runs for product3 → "Keyboard"
Controlling the Chain with Null
If a resolver returns null, GraphQL stops the chain at that point. Child resolvers never run for a null parent, and the field in the response becomes null (or triggers an error if the field is non-nullable).
Query.store returns null
→ Store.name never runs
→ Store.manager never runs
→ Store.products never runs
→ Response: { "store": null }
Key Points
- Each resolver's return value becomes the
parentargument for all child resolvers below it. - Child resolvers only run when the client requests those fields — unasked fields generate no database calls.
- When a resolver returns a list, child resolvers run once per item in that list.
- A null return from a resolver stops the chain — child resolvers for that branch never execute.
