Databaseshigh

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.

Memory anchor

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 (...)).

Expected depth

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.

Deep — senior internals

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.

🎤Interview-ready answer

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.

Common trap

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.

Related concepts