AboutBlogContact
Software ArchitectureMarch 14, 2018 2 min read 160Updated: June 22, 2026

GraphQL and the N+1 Problem: Solving it with DataLoader (2018)

AunimedaAunimeda
📋 Table of Contents

GraphQL and the N+1 Problem: Solving it with DataLoader

It’s 2018, and everyone is moving from REST to GraphQL. The promise is enticing: "Fetch exactly what you need, and nothing more." But there's a performance trap waiting for the unwary: the N+1 query problem.

What is the N+1 Problem?

Imagine a query to fetch 10 posts and their authors:

query {
  posts {
    title
    author {
      name
    }
  }
}

A naive resolver implementation would look like this:

  1. One query to fetch all posts (1 query).
  2. For each post, a separate query to fetch the author (N queries).

If you have 100 posts, you just made 101 database queries for a single request!

The Solution: DataLoader

Facebook released DataLoader, a library that uses batching and caching to solve this. Instead of fetching each author individually, DataLoader "waits" until the end of the event loop tick, collects all the requested IDs, and fetches them in a single batch query.

const DataLoader = require('dataloader');

// 1. Create a batch loading function
const batchUsers = async (ids) => {
  console.log(`Fetching IDs: ${ids}`); // Should only log once!
  const users = await db.users.find({ id: { $in: ids } });
  
  // Important: The returned array must match the order of IDs
  return ids.map(id => users.find(user => user.id === id));
};

// 2. Instantiate the loader per request
const userLoader = new DataLoader(batchUsers);

// 3. Use it in your resolver
const resolvers = {
  Post: {
    author: (post) => userLoader.load(post.authorId)
  }
};

How it Works Under the Hood

DataLoader uses process.nextTick() (in Node.js) to schedule the batch function. When userLoader.load(id) is called:

  1. It pushes the ID into a queue.
  2. It returns a Promise.
  3. Once the current execution stack is empty, the batch function is called with all queued IDs.
  4. The individual Promises are resolved with the results from the batch.

Memoization Cache

DataLoader also has a built-in cache. If you request the same author multiple times in the same request, it will return the cached Promise immediately without calling the batch function.

In 2018, building a GraphQL API without DataLoader is like driving a car with the handbrake on. It’s an essential tool for any production-grade backend.


Aunimeda designs and builds scalable software architectures - from system design to implementation and ongoing engineering.

Contact us to discuss architecture for your project. See also: Custom Software Development, Web Development

Read Also

Partytown: Offloading Third-Party Scripts to Web Workers (2021)aunimeda
Software Architecture

Partytown: Offloading Third-Party Scripts to Web Workers (2021)

Third-party scripts are killing your Lighthouse score. In 2021, Partytown offers a radical solution: move them all to a Web Worker and proxy the DOM.

GraphQL Schema Design: Thinking in Graphs, Not Endpoints (2015)aunimeda
Software Architecture

GraphQL Schema Design: Thinking in Graphs, Not Endpoints (2015)

Facebook just open-sourced GraphQL. It's time to stop thinking in REST resources and start thinking in edges and nodes. Let's design a Relay-compatible schema.

The Price of Abstraction: Re-evaluating the 'Clean Code' Myths of 2018aunimeda
Software Architecture

The Price of Abstraction: Re-evaluating the 'Clean Code' Myths of 2018

In 2018, we over-engineered for 'future flexibility' that never arrived. Today, we prioritize code locality and the 'Grokability' factor. Explore why we moved from deep inheritance and HOCs to flat, predictable composition.

Need IT development for your business?

We build websites, mobile apps and AI solutions. Free consultation.

Get Consultation All articles