DataLoader & N+1 Problem

◕ GraphQLLesson 11Advanced

The N+1 problem is GraphQL's most notorious performance pitfall. When fetching a list of items each with nested data, naive resolvers fire one database query per item. DataLoader batches and caches these calls into a single query.

1 The N+1 Problem Illustrated
GraphQL Query that causes N+1
# This innocent-looking query causes N+1 database queries
{
  posts {           # 1 query: SELECT * FROM posts
    title
    author {        # N queries: SELECT * FROM users WHERE id = ?
      name          # One query per post!
    }
  }
}

# With 100 posts: 1 + 100 = 101 database queries!
# With DataLoader:  1 + 1   = 2 database queries
2 Fixing with DataLoader
JavaScript — DataLoader Setup
const DataLoader = require("dataloader");

// Batch function — receives array of IDs, returns array of results
async function batchUsers(userIds) {
  // ONE query for all IDs at once
  const users = await User.find({ _id: { $in: userIds } });
  // Must return in same order as input IDs!
  const userMap = {};
  users.forEach(u => { userMap[u.id] = u; });
  return userIds.map(id => userMap[id] || null);
}

// Create per-request DataLoader (never share between requests!)
function createLoaders() {
  return {
    userLoader:    new DataLoader(batchUsers),
    commentLoader: new DataLoader(batchCommentsByPostId)
  };
}

// In Apollo Server context
context: async ({ req }) => ({
  currentUser: await getUser(req),
  loaders: createLoaders()  // Fresh loaders per request
});

// In resolver — uses loader instead of direct DB call
const resolvers = {
  Post: {
    author: (post, _, { loaders }) => {
      return loaders.userLoader.load(post.authorId); // batched automatically!
    }
  }
};
3 Code Challenge
Challenge: Add a commentLoader DataLoader that batches comment fetching by postId. Use it in the Post.comments resolver. Compare the number of DB queries before and after (use Mongoose's debug mode to see queries).