Bird
Raised Fist0
GraphQLquery~10 mins

DataLoader batching and caching in GraphQL - Step-by-Step Execution

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Concept Flow - DataLoader batching and caching
Request 1 for key A
Request 2 for key B
Batch keys A, B
Single batch fetch from DB
Return results for A, B
Cache results for A, B
Request 3 for key A
Return cached result for A
DataLoader collects multiple requests, batches them into one database call, caches results, and returns cached data for repeated requests.
Execution Sample
GraphQL
const loader = new DataLoader(keys => batchLoad(keys));

loader.load('A');
loader.load('B');
// batchLoad called once with ['A','B']
loader.load('A'); // returns cached result
This code batches two loads into one DB call and caches the result for repeated keys.
Execution Table
StepActionKeys RequestedBatch Triggered?DB Call KeysCache StateReturned Result
1loader.load('A') called['A']No[]{}Promise pending
2loader.load('B') called['A','B']Yes['A','B']{}Promise pending
3batchLoad(['A','B']) called['A','B']Yes['A','B']{}Fetching from DB
4DB returns results['A','B']No[]{"A": "resultA", "B": "resultB"}Results for A and B
5loader.load('A') called again['A']No[]{"A": "resultA", "B": "resultB"}Returns cached resultA
6loader.load('C') called['C']Yes['C']{"A": "resultA", "B": "resultB"}Promise pending
7batchLoad(['C']) called['C']Yes['C']{"A": "resultA", "B": "resultB"}Fetching from DB
8DB returns result for C['C']No[]{"A": "resultA", "B": "resultB", "C": "resultC"}Result for C
9Execution ends-----
💡 No more load calls; all requested keys fetched and cached.
Variable Tracker
VariableStartAfter Step 2After Step 4After Step 5After Step 8Final
keysRequested[]['A','B']['A','B']['A']['C'][]
cache{}{}{"A": "resultA", "B": "resultB"}{"A": "resultA", "B": "resultB"}{"A": "resultA", "B": "resultB", "C": "resultC"}{"A": "resultA", "B": "resultB", "C": "resultC"}
dbCalls001122
Key Moments - 3 Insights
Why does loader.load('A') not trigger a new DB call after the first batch?
Because the result for 'A' is cached after the first batch (see step 4 and step 5 in execution_table), so DataLoader returns the cached result immediately without calling the DB again.
How does DataLoader know when to batch multiple keys together?
DataLoader batches keys requested within the same event loop tick before calling the batchLoad function (see steps 1 and 2 where keys 'A' and 'B' are collected before the DB call in step 3).
What happens if a new key is requested after the cache has some keys?
DataLoader batches only the new keys not in cache (see step 6 and 7 where 'C' is requested and batchLoad is called only for ['C']).
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table at step 5. What does loader.load('A') return?
ACached result for 'A'
BTriggers a new DB call
CReturns undefined
DReturns a promise pending DB call
💡 Hint
Check the 'Cache State' and 'Returned Result' columns at step 5 in execution_table.
At which step does DataLoader batch keys 'A' and 'B' together for a single DB call?
AStep 2
BStep 4
CStep 3
DStep 5
💡 Hint
Look at the 'Batch Triggered?' and 'DB Call Keys' columns in execution_table.
If loader.load('B') was called again after step 5, what would happen?
AA new DB call for 'B' would be made
BThe cached result for 'B' would be returned
CAn error would occur
DIt would batch with 'C' automatically
💡 Hint
Refer to the cache state after step 5 in variable_tracker and execution_table.
Concept Snapshot
DataLoader batches multiple key requests into one DB call.
It caches results to avoid repeated DB calls for same keys.
Keys requested in the same tick are grouped.
Repeated requests return cached results immediately.
Batch function receives array of keys and returns results array.
Improves efficiency by reducing DB round-trips.
Full Transcript
DataLoader helps optimize database requests by batching multiple key requests into a single call and caching results. When you call loader.load('A') and loader.load('B') quickly, DataLoader waits to collect these keys and then calls the batch function once with both keys. The database returns results for both keys, which DataLoader caches. Later, if you request 'A' again, DataLoader returns the cached result immediately without calling the database again. This process reduces the number of database calls and improves performance. The execution table shows each step: initial requests, batching, database calls, caching, and returning cached results. Key moments include understanding when batching happens, why cached results prevent new DB calls, and how new keys after caching are handled. The visual quiz tests understanding of these steps. The snapshot summarizes the main points for quick recall.

Practice

(1/5)
1. What is the main purpose of using DataLoader in a GraphQL server?
easy
A. To batch multiple data requests into a single request and cache results during one operation
B. To replace the database with an in-memory store
C. To generate GraphQL schema automatically
D. To handle user authentication and authorization

Solution

  1. Step 1: Understand DataLoader's role in GraphQL

    DataLoader groups many small data requests into fewer big ones to reduce database calls.
  2. Step 2: Identify caching behavior

    It also caches results during one operation to avoid duplicate requests for the same data.
  3. Final Answer:

    To batch multiple data requests into a single request and cache results during one operation -> Option A
  4. Quick Check:

    Batching + caching = To batch multiple data requests into a single request and cache results during one operation [OK]
Hint: Remember: DataLoader batches and caches requests [OK]
Common Mistakes:
  • Thinking DataLoader replaces the database
  • Confusing DataLoader with schema generation tools
  • Assuming it handles authentication
2. Which of the following is the correct way to create a new DataLoader instance in JavaScript?
easy
A. const loader = DataLoader(batchLoadFn);
B. const loader = new DataLoader(batchLoadFn);
C. const loader = DataLoader.new(batchLoadFn);
D. const loader = new DataLoader.load(batchLoadFn);

Solution

  1. Step 1: Recall DataLoader instantiation syntax

    DataLoader is a class and must be instantiated with the new keyword.
  2. Step 2: Check method usage

    The constructor takes a batch loading function as argument, so new DataLoader(batchLoadFn) is correct.
  3. Final Answer:

    const loader = new DataLoader(batchLoadFn); -> Option B
  4. Quick Check:

    Use new with DataLoader class [OK]
Hint: Always use 'new' keyword to create DataLoader [OK]
Common Mistakes:
  • Omitting 'new' keyword
  • Using incorrect method calls like .new or .load
  • Calling DataLoader as a function without 'new'
3. Given the following DataLoader usage, what will be the output?
const DataLoader = require('dataloader');

const batchLoadFn = async keys => keys.map(key => key * 2);
const loader = new DataLoader(batchLoadFn);

(async () => {
  const result1 = await loader.load(2);
  const result2 = await loader.load(3);
  console.log([result1, result2]);
})();
medium
A. [4, 6]
B. [2, 3]
C. [NaN, NaN]
D. [undefined, undefined]

Solution

  1. Step 1: Understand batchLoadFn behavior

    The batch function doubles each key: for key 2 returns 4, for key 3 returns 6.
  2. Step 2: Analyze loader.load calls

    Calling loader.load(2) and loader.load(3) triggers batchLoadFn with keys [2,3], returning [4,6].
  3. Final Answer:

    [4, 6] -> Option A
  4. Quick Check:

    Keys doubled = [4, 6] [OK]
Hint: Batch function maps keys to doubled values [OK]
Common Mistakes:
  • Expecting original keys as output
  • Confusing async behavior with undefined
  • Ignoring batch function logic
4. Identify the error in this DataLoader usage code snippet:
const DataLoader = require('dataloader');

const batchLoadFn = async keys => {
  return keys.map(key => fetchUserFromDB(key));
};

const loader = new DataLoader(batchLoadFn);

loader.load(1).then(user => console.log(user));
Assuming fetchUserFromDB returns a Promise resolving to user data.
medium
A. DataLoader must be called without 'new' keyword
B. fetchUserFromDB should not return a Promise
C. loader.load should be called with an array of keys, not a single key
D. batchLoadFn should return a Promise resolving to an array, but it returns an array of Promises

Solution

  1. Step 1: Check batchLoadFn return type

    batchLoadFn returns an array of Promises because fetchUserFromDB returns a Promise for each key.
  2. Step 2: Understand DataLoader batch function requirement

    DataLoader expects batchLoadFn to return a single Promise resolving to an array of results, not an array of Promises.
  3. Final Answer:

    batchLoadFn should return a Promise resolving to an array, but it returns an array of Promises -> Option D
  4. Quick Check:

    Return a Promise of array, not array of Promises [OK]
Hint: Batch function must return Promise resolving array, not array of Promises [OK]
Common Mistakes:
  • Returning array of Promises instead of Promise of array
  • Misusing 'new' keyword with DataLoader
  • Passing single key instead of array to batch function
5. You want to optimize a GraphQL server that fetches posts and their authors. You use DataLoader for users and posts. Which approach best uses DataLoader's batching and caching to avoid redundant database calls?
hard
A. Create a new DataLoader instance for each field resolver call
B. Create a global DataLoader instance shared across all requests to cache all users and posts forever
C. Create one DataLoader instance per request for users and posts, and use load for each user or post ID
D. Call database queries directly without DataLoader to avoid complexity

Solution

  1. Step 1: Understand DataLoader lifecycle

    DataLoader instances should be created per request to cache data only during that request and avoid stale data.
  2. Step 2: Use DataLoader to batch and cache IDs

    Using load for each user or post ID lets DataLoader batch requests and cache results within the request.
  3. Final Answer:

    Create one DataLoader instance per request for users and posts, and use load for each user or post ID -> Option C
  4. Quick Check:

    Per-request DataLoader + load calls = Create one DataLoader instance per request for users and posts, and use load for each user or post ID [OK]
Hint: Create DataLoader per request, not globally or per field [OK]
Common Mistakes:
  • Sharing DataLoader globally causing stale cache
  • Creating new DataLoader per field causing no batching
  • Skipping DataLoader and querying DB repeatedly