DataLoader and N+1 problem in GraphQL

From the GraphQL cheat sheet · Resolvers · verified Jul 2026

DataLoader and N+1 problem

javascript
// N+1 Problem Example (BAD)
const resolvers = {
  Query: {
    posts: async (parent, args, { db }) => {
      return await db.post.findMany(); // 1 query
    }
  },
  Post: {
    author: async (parent, args, { db }) => {
      // This runs once per post! (N queries)
      return await db.user.findUnique({
        where: { id: parent.authorId }
      });
    }
  }
};
// Result: 1 + N queries (if 100 posts, 101 queries!)

// Solution with DataLoader (GOOD)
const DataLoader = require('dataloader');

// Create loaders
function createLoaders(db) {
  return {
    user: new DataLoader(async (userIds) => {
      // Batch load all users in one query
      const users = await db.user.findMany({
        where: { id: { in: userIds } }
      });
      
      // Map back to original order
      const userMap = new Map();
      users.forEach(user => userMap.set(user.id, user));
      return userIds.map(id => userMap.get(id));
    }),
    
    postsByUser: new DataLoader(async (userIds) => {
      // Batch load all posts
      const posts = await db.post.findMany({
        where: { authorId: { in: userIds } }
      });
      
      // Group by user
      const postMap = new Map();
      userIds.forEach(id => postMap.set(id, []));
      posts.forEach(post => {
        const userPosts = postMap.get(post.authorId);
        userPosts.push(post);
      });
      
      return userIds.map(id => postMap.get(id));
    })
  };
}

// Use in context - loaders attach via the integration (Apollo Server 4+)
const server = new ApolloServer({ typeDefs, resolvers });

app.use('/graphql', cors(), express.json(), expressMiddleware(server, {
  context: async ({ req }) => ({
    db,
    loaders: createLoaders(db) // New instance per request
  })
}));

// Use in resolvers
const resolvers = {
  Post: {
    author: async (parent, args, { loaders }) => {
      // Automatically batched!
      return await loaders.user.load(parent.authorId);
    }
  },
  User: {
    posts: async (parent, args, { loaders }) => {
      // Batched and cached
      return await loaders.postsByUser.load(parent.id);
    }
  }
};
// Result: Just 2 queries total!
💡 DataLoader batches and caches requests
⚡ Solves N+1 query problem
📌 Automatic request deduplication
🔥 Per-request caching

More GraphQL tasks

Back to the full GraphQL cheat sheet