How it works
The explanation, with examples you can run.
Seeing it
Press Run to see what your code prints, in order.
Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.
The naive resolver asks for u1 twice. The loader asks once, for three distinct ids, because the cache returns the same promise for a repeated key. This toy waits with setTimeout; DataLoader itself coalesces the loads made in one tick of the event loop (it schedules the batch after the promise jobs already queued, and lets you plug in your own scheduler), so loads made while one level of the query is being resolved end up in one batch, and the next level forms the next batch.
Using DataLoader
import DataLoader from "dataloader" function createContext(req) { return { user: authenticate(req), loaders: { user: new DataLoader(async (ids) => { const rows = await sql`SELECT * FROM users WHERE id = ANY(${ids})` const byId = new Map(rows.map((r) => [r.id, r])) return ids.map((id) => byId.get(id) ?? null) // one result per key, in key order }), }, }} const resolvers = { Post: { author: (post, _args, ctx) => ctx.loaders.user.load(post.authorId) },}Three rules matter. The batch function returns an array of the same length as the keys, in the same order. Loaders are created per request, because their cache is request-scoped memoisation: share one across requests and users see each other's data, or stale data forever. And batching reduces round trips, not work: a query that asks for ten thousand comments still loads ten thousand comments, which is why pagination and cost limits come next.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.