Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
30 Aug 2025 · 6 min read ·Article 61 / 125
Go

61 Nested Resolvers and Chained Resolvers

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

61 Nested Resolvers and Chained Resolvers: Understanding the Mechanics and Best Practices in GraphQL (Complete with Examples & Diagrams)


GraphQL has revolutionized the way we design APIs—one of its main strengths is the ability to write complex queries that can pull layered data in a single request. But beneath the beauty of this syntax lies a crucial concept that every engineer must understand: resolvers and how they work, especially in the context of nested resolvers and chained resolvers.

This time, we’re going to take a deep dive into nested resolvers and chained resolvers in GraphQL—from the basic concepts, code simulations, and flow diagrams, all the way to best practices. This article is perfect for those who want to strengthen their fundamentals while building efficient and scalable APIs.


Resolver 101: The Foundation

Before going deep into “nested” and “chained” resolvers, you need to understand the root of the matter: What is a resolver?

Put simply, a resolver is a function responsible for “filling in” the value of a field requested in a GraphQL query. When GraphQL receives a request, it traverses the query based on the schema, then calls resolvers according to that structure.

A Simple Resolver Example

Consider the following schema:

graphql
 1type Book {
 2  id: ID!
 3  title: String!
 4  author: Author!
 5}
 6
 7type Author {
 8  id: ID!
 9  name: String!
10}
11
12type Query {
13  book(id: ID!): Book
14}

Query:

graphql
1{
2  book(id: 1) {
3    title
4    author {
5      name
6    }
7  }
8}

Resolver implementation:

js
 1const books = [
 2  { id: "1", title: "GraphQL in Depth", authorId: "2" }
 3];
 4const authors = [
 5  { id: "2", name: "Adi Nugroho" }
 6];
 7
 8const resolvers = {
 9  Query: {
10    book: (_, { id }) => books.find(b => b.id === id),
11  },
12  Book: {
13    author: (parent) => authors.find(a => a.id === parent.authorId)
14  }
15}

Notice how author is resolved by a separate function on the Book type—that is a nested resolver.

1. Nested Resolver: Resolvers Within Resolvers

Definition

A nested resolver is a resolver function declared at the level of a field nested inside an object type.

When a query requests layered data, GraphQL calls resolvers:

  • Starting from the root level (e.g., book in Query),
  • Passing through each requested field,
  • For fields with an object type (not a primitive type), it calls the resolver on the next field type,
  • And so on, recursively.

Flow Illustration

Let’s look at the following mermaid diagram:

MERMAID
graph TD
    Start([Query: book(id:1)]) --> B[Resolver: Query.book]
    B --> T[Return Book object]
    T --> C[Resolver: Book.title (primitive)]
    T --> D[Resolver: Book.author (object)]
    D --> E[Resolver: Author.name (primitive)]

Explanation:

  • Query.book looks up the book by ID (returns a JSON object).
  • For the title field, since it’s a primitive, the value is taken directly from the object.
  • For the author, since its type is the object Author, the resolver on the author field of the Book object is called, then the process repeats deeper inside.

Request Simulation & Process Table

StepFieldCurrent Parent ObjectResolver CalledOutput
1Query.booknullQuery.book{id,title,authorId}
2Book.title{id,title,authorId}Default/simple"GraphQL in Depth"
3Book.author{id,title,authorId}Book.author{id, name}
4Author.name{id, name}Default/simple"Adi Nugroho"

2. Chained Resolvers: Serialization Between Resolvers

A common question arises: Is the result of one resolver passed to the next resolver?

The answer: YES, but limited to the parent field currently being processed. Chained resolvers mean that each resolver waits for the result (object) of the resolver above it. This is what enables dynamic data and computed fields.

Case Study: Resolver Chaining

Suppose we want to count the number of books owned by an Author in a new field called bookCount:

Schema

graphql
1type Author {
2  id: ID!
3  name: String!
4  bookCount: Int!
5}

Resolver Chain

js
1const resolvers = {
2  Query: {
3    author: (_, { id }) => authors.find(a => a.id === id),
4  },
5  Author: {
6    bookCount: (parent) => books.filter(b => b.authorId === parent.id).length,
7    // you could also create a 'name' resolver if needed
8  }
9}

Resolver Serialization Flow Diagram

MERMAID
graph TD
    Q([Query: author(id:2)]) --> R1[Resolver: Query.author]
    R1 --> AO[Return Author object]
    AO --> R2[Resolver: Author.bookCount]
    R2 --> R3[Count & Return]

Note: The return value of the Query.author resolver becomes the input (parent) for the Author.bookCount resolver—this is the “chain” between resolvers.


3. The Challenge: The N+1 Problem and Optimization

Nested resolvers are very powerful, but they can cause a classic issue: the N+1 Problem.

Example Case

Query: All books, complete with the author’s name:

graphql
1{
2  books {
3    title
4    author {
5      name
6    }
7  }
8}
  • The books resolver fetches all books (N of them).
  • For each book, it fetches the author (resulting in N more queries).

The problem? If the dataset is large, the API will “flood” the database!

Simulation with DataLoader

The solution: Use DataLoader (batching and caching).

js
 1const DataLoader = require('dataloader');
 2const authorLoader = new DataLoader(async (ids) => {
 3  // ids: array of authorId
 4  const result = await db.authors.find({ id: { $in: ids } });
 5  return ids.map(id => result.find(a => a.id === id));
 6});
 7
 8const resolvers = {
 9  Query: {
10    books: () => books,
11  },
12  Book: {
13    author: (parent) => authorLoader.load(parent.authorId)
14  }
15}

4. Best Practices for Defining Nested and Chained Resolvers

  • Minimize fetching in every child resolver. “Delegate” data fetching as much as possible to the parent/root resolver.
  • Optimize with batching/caching (e.g., DataLoader in Node.js).
  • Make resolvers stateless and idempotent.
  • Modularize resolvers: Separate fetching, transformation, and aggregation logic.

5. Comparison Summary Table

AspectNested ResolverChained Resolver
DefinitionResolver on a nested fieldSerial resolver (depends on parent)
ExampleBook.authorAuthor.bookCount
InputParent object, argsParent object from the previous resolver
RiskN+1 query problemData redundancy if the chain goes deep
SolutionBatching, DataLoader, eager loadingOptimization at the root fetch

Conclusion

Understanding the mechanics of nested resolvers and chained resolvers is a must for any backend engineer who wants to build efficient and scalable GraphQL APIs. By understanding the architecture of how resolvers are called in a layered and serial fashion, we can optimize data fetching, avoid the N+1 problem, and make our code more modular.

GraphQL is indeed powerful, but it demands discipline in structuring resolvers—whether nested or chained. With the simulations above, you should be ready to build smart APIs! Don’t hesitate to use batching, instrumentation, as well as diagrams and flows to help your team understand the data flow across all resolvers.

Happy optimizing, and may your GraphQL code become ever more effective!

Related Articles

💬 Comments