N+1 Query Problem
The N+1 problem occurs when code fetches a list of N items, then makes a separate database query for each item to fetch related data, resulting in N+1 total queries instead of 1-2.
N+1 = asking a librarian for 100 books, then making 100 separate trips to ask 'who wrote this one?' DataLoader is the smart librarian who collects all your author questions, waits a beat, then answers them all with ONE trip to the card catalog (SELECT WHERE id IN (...)).
Classic example: fetch 100 blog posts, then for each post fetch its author—101 queries. The fix: use a JOIN query to fetch posts with authors in one query, or use a dataloader pattern to batch author queries into a single IN clause query. ORMs with eager loading (Sequelize include, TypeORM relations with eager: true) solve this at the ORM layer. But ORMs with lazy loading (the default in many ORMs) are the root cause—accessing a relation property triggers a query.
The DataLoader pattern (from Facebook's dataloader library) batches and caches queries: instead of querying for user 1, user 2, user 3 independently, DataLoader collects all user IDs requested in a single event loop tick and issues one query: SELECT * FROM users WHERE id IN (1, 2, 3). Per-request DataLoader instances ensure caching is request-scoped (avoiding stale data across requests). DataLoader is the standard solution for GraphQL resolvers where each resolver field independently fetches data. In REST APIs, prefer query-level joins or SQL window functions to avoid multiple round trips.
N+1 occurs when fetching a list triggers per-item queries for related data. Solutions: (1) SQL JOINs to fetch relations in one query, (2) eager loading in ORMs, (3) DataLoader pattern to batch independent lookups into a single IN-clause query. DataLoader is essential in GraphQL resolvers. Always check the number of queries with ORM query logging when developing features involving relations.
ORM lazy loading is the silent killer—accessing a relation property like `post.author` outside an explicitly loaded eager context issues a new SQL query invisibly. In a loop over 1000 posts, this creates 1000 queries with no error, just a slow endpoint. Always enable ORM query logging in development and set a query count alert in staging to catch N+1 regressions before production.