# Solving N+1 With DataLoader — GraphQL

Source: https://www.skillbyai.com/en/graphql/r-dataloader

> Batching and per-request caching.

## Collect keys, load once

Naive resolvers cause the **N+1 problem**: fetching 50 orders, then calling the product resolver for each item, triggers dozens of separate database queries. **DataLoader** (and equivalents in other languages) collects all keys requested during one tick of execution, calls a **batch function** once with every key (for example `WHERE id IN (...)`), and caches results for the rest of the request. Create new loaders per request so caches never leak between users.

## A product loader

JavaScript using the dataloader package (a sketch).

```javascript
import DataLoader from "dataloader";

function createLoaders(db) {
  return {
    productById: new DataLoader(async (ids) => {
      const rows = await db.query("SELECT * FROM products WHERE id = ANY($1)", [ids]);
      const byId = new Map(rows.map((r) => [String(r.id), r]));
      return ids.map((id) => byId.get(String(id)) ?? null);   // same order as keys
    }),
  };
}

// context: ({ req }) => ({ loaders: createLoaders(db), ... })
const resolvers = {
  OrderItem: {
    product: (item, _args, { loaders }) => loaders.productById.load(item.productId),
  },
};
```

## Return results in key order

The batch function must return one result per key, in the same order, using null for missing ones.

**Quiz:** What does DataLoader do?

- [x] Batches many individual loads into one call and caches them per request
- [ ] Encrypts queries
- [ ] Generates the schema
- [ ] Replaces the database

*Answer:* Batches many individual loads into one call and caches them per request. Fixes N+1 queries.
