Dataloader 3.0: A new algorithm to solve the N+1 Problem - WunderGraph

Jens Neuse
CEO & Co-Founder at WunderGraph
September 27, 2023·31min read
Last updated on September 9, 2025

State of GraphQL Federation 2026

How are teams governing schema changes, handling production traffic, and measuring Federation success? Share your experience and get early access to the full report. For every valid survey completed, we'll donate $30 to UNICEF.

While implementing Cosmo Router, an open-source replacement for Apollo Router, we ran into the problem of keeping our code for solving the N+1 Problem maintainable...

To solve this problem, we developed a new algorithm that solves the N+1 Problem in way that's much more efficient and easier to maintain than our previous solution, which was based on the DataLoader pattern that's commonly used in the GraphQL community. Instead of resolving depth-first, we load the data breadth-first, which allows us to reduce concurrency from O(N^2) to O(1) and improve performance by up to 5x while reducing code complexity.

If you're interested in checking out the code, you can find it on GitHub.

The N+1 Problem

The N+1 Problem is a common problem in GraphQL that occurs when you have a list of items and you need to fetch additional data for each item in the list. Let's look at an example to illustrate this problem:

First, we define four subgraphs:

# Products
type Query {
  topProducts: [Product!]
}

type Product @key(fields: "upc") {
  upc: String!
  name: String!
}

# Inventory
type Product @key(fields: "upc") {
  upc: String! @external
  stock: Int!
}

# Accounts
type User @key(fields: "id") {
  id: ID!
  name: String
}

# Reviews
type Review @key(fields: "id") {
  id: ID!
  body: String
  author: User
}

type User @key(fields: "id") {
  id: ID! @external
}

type Product @key(fields: "upc") {
  upc: String! @external
  reviews: [Review]
}

Next, we define a query that fetches the top products and their reviews:

query {
  topProducts {
    # returns a list of products from the Products subgraph
    name
    stock # returns the stock from the Inventory subgraph
    reviews {
      # returns a list of reviews from the Reviews subgraph
      body
      author {
        # returns the author from the Accounts subgraph
        name
      }
    }
  }
}

So where does the N+1 Problem got its name from? The +1 refers to the first request to fetch the topProducts from the Products subgraph. The N refers to the number of additional requests that are required to fetch the associated data for each product. Let's go through the steps of resolving this Query to see what this means in practice.

Let's assume the topProducts field returns 3 products...

The DataLoader pattern is a common solution to solve the N+1 Problem in GraphQL. It's based on the idea of batching requests within lists to reduce the number of requests...

The Problem with the DataLoader Pattern

At surface level, the DataLoader pattern seems like a great solution to solve the N+1 Problem...

A New Algorithm to Solve the N+1 Problem - Breadth-First data loading

Most if not all GraphQL servers resolve fields depth-first. This means, they resolve a field and all its subfields before they resolve the next sibling field...

The Benefits of Breadth-First data loading

Now that we've looked at the algorithm in detail, let's talk about the benefits of breadth-first data loading.

We can easily debug our code again, and the whole implementation is a lot easier to understand and maintain. In fact, our initial implementation was not entirely complete, but we were able to quickly fix and extend it as the code was easy to reason about.

Conclusion

The DataLoader pattern is a great solution to solve the N+1 Problem...

So does this really just affect federated GraphQL Routers, or GraphQL Gateways, or even GraphQL servers in general?

I think we should all be aware of the trade-offs we're making when using the DataLoader pattern.