Registry / http-networking / dataloader

dataloader

JSON →
library2.0jsnpmunverified

DataLoader is a JavaScript utility designed to optimize data fetching from various backend sources like databases or web services. It achieves this by intelligently batching multiple individual data requests into a single operation and caching results, significantly reducing the number of round-trips to the backend. The current stable version is 2.2.3, with minor patch releases occurring regularly to address fixes and small improvements. Major versions, like v2.0.0, introduce breaking changes and significant architectural updates, such as becoming part of the GraphQL Foundation. A key differentiator is its core focus on solving the N+1 problem by providing a simple, consistent API that coalesces requests within a single event loop tick, making it particularly valuable in GraphQL server implementations where diverse data requirements are common. It ships with TypeScript types, ensuring robust development in typed environments and is generally released on an as-needed basis rather than a fixed cadence.

npm install dataloader
INSTALL
IMPORT
SIG · DATALOADER
D
dataloader
http-networkingjavascriptv2.0
Install
—
Import
—
Disk
—
Pass rate
0/ 6
Env Coverage0 / 6
glibc
18–22
musl
18–22
Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
node 18–226 runs
build_error
glibc
node 18–226 runs
build_error
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

DataLoader
✓ import { DataLoader } from 'dataloader';
✗ const DataLoader = require('dataloader');
Preferred ESM import for modern Node.js and bundlers. The CommonJS `require` pattern is also valid but less idiomatic for newer projects.
BatchLoadFn
✓ import type { BatchLoadFn } from 'dataloader';
Type import for the batch loading function signature, useful for TypeScript development.
DataLoader
✓ const DataLoader = require('dataloader');
CommonJS import for Node.js environments that do not support or are not configured for ESM. This will correctly import the CJS export.

This example demonstrates creating a DataLoader instance, performing batched requests for multiple user IDs within a single event loop tick, chaining requests, and retrieving cached values, showing how it minimizes backend calls.

import { DataLoader } from 'dataloader'; interface User { id: number; name: string; invitedByID?: number; } // Simulate a backend API call that fetches users by a batch of IDs async function myBatchGetUsers(ids: readonly number[]): Promise<(User | Error)[]> { console.log(`[DB] Fetching users with IDs: ${ids.join(', ')}`); // In a real app, this would be a single database query or API call const users: User[] = ids.map(id => ({ id, name: `User ${id}`, invitedByID: id > 1 ? id - 1 : undefined // Example for chaining loads })); // Return in the same order as requested keys return ids.map(id => users.find(u => u.id === id) || new Error(`User ${id} not found`)); } async function main() { // Create a new DataLoader instance per request (or execution context) const userLoader = new DataLoader<number, User>(myBatchGetUsers, { cache: true, // Caching is enabled by default, explicitly shown for clarity cacheKeyFn: (key) => `user:${key}` // Custom cache key for clarity, default works for primitives }); console.log('--- Initiating parallel loads (will be batched) ---'); const [user1, user2] = await Promise.all([ userLoader.load(1), userLoader.load(2) ]); if (user1 instanceof Error || user2 instanceof Error) { console.error('Error loading users:', user1, user2); return; } console.log(`Loaded User 1: ${user1.name}`); console.log(`Loaded User 2: ${user2.name}`); console.log('\n--- Chaining loads (will be batched if in same tick) ---'); const invitedBy1 = await userLoader.load(user1.invitedByID || 0); // Assuming user1 has invitedByID const invitedBy2 = await userLoader.load(user2.invitedByID || 0); // Assuming user2 has invitedByID if (invitedBy1 instanceof Error || invitedBy2 instanceof Error) { console.error('Error loading invitedBy users:', invitedBy1, invitedBy2); return; } console.log(`User ${user1.id} was invited by ${invitedBy1.name}`); console.log(`User ${user2.id} was invited by ${invitedBy2.name}`); console.log('\n--- Loading cached value (no DB call) ---'); const cachedUser1 = await userLoader.load(1); console.log(`Loaded cached User 1: ${cachedUser1 instanceof Error ? 'Error' : cachedUser1.name}`); } main().catch(console.error);
Debug
Known issues
breakingAs of v2.0.0, the `.loadMany()` method now returns an Array which may contain `Error` objects for individual keys that failed, rather than throwing a single error for the entire batch. Consumers must now explicitly check for `Error` instances within the returned array.
fix
Update `.loadMany()` result handling: `const results = await loader.loadMany([1,2,3]); results.forEach(res => { if (res instanceof Error) { /* handle error */ } else { /* handle value */ } });`
affects: >=2.0.0
gotchaDataLoader instances typically represent a unique cache and context. In web servers (e.g., Express, GraphQL), a new DataLoader instance should be created per-request to prevent data leaks between different users and ensure appropriate caching semantics for mutable data. Do not use a single global DataLoader instance across all requests.
fix
Instantiate `new DataLoader(...)` within the scope of each incoming request (e.g., inside a middleware or request handler function).
affects: >=1.0.0
gotchaDataLoader's batching mechanism coalesces individual `load()` calls that occur within a single 'tick' of the JavaScript event loop. If you `await` a `load()` call before making another, the second `load()` call will not be batched with the first, potentially leading to N+1 queries. Structure your code to defer `await` until all desired `load()` calls have been queued, often by using `Promise.all()`.
fix
Instead of `const a = await loader.load(keyA); const b = await loader.load(keyB);`, use `const [a, b] = await Promise.all([loader.load(keyA), loader.load(keyB)]);` to ensure concurrent loads are batched.
affects: >=1.0.0
gotchaThe batch loading function provided to DataLoader *must* return a Promise that resolves to an Array of values (or Errors) that is exactly the same length as the input `keys` Array, and in the *same order* as the input keys. Failing to adhere to this contract will result in an `Invariant Violation` error.
fix
Ensure your batch loading function maps each input key to its corresponding result or an Error object, preserving the original order and array length. Example: `keys.map(key => myMap.get(key) || new Error('NotFound'))`.
affects: >=1.0.0
gotchaWhen using complex objects as keys for `loader.load()`, JavaScript's default Map behavior uses reference equality. If you pass different object instances with the same logical content, they will be treated as distinct keys, bypassing the cache. To address this, provide a custom `cacheKeyFn`.
fix
Initialize DataLoader with `{ cacheKeyFn: key => JSON.stringify(key) }` or a function that produces a unique, serializable string from your object keys.
affects: >=1.1.0
deprecatedWhile DataLoader gained direct browser support in v1.4.0, it explicitly notes that it "cannot rely on the same post-promise job queuing behavior that allows for best performance in Node environments." This implies a fallback behavior is used, and performance characteristics in the browser might differ.
fix
Be mindful of performance expectations when using DataLoader in a browser environment, as its batching might not be as efficient as in Node.js due to differences in event loop scheduling. Test thoroughly in target browser environments.
affects: >=1.4.0
Errors
Common errors & fixes
TypeError: DataLoader is not a constructor
Attempting to use `new DataLoader()` after importing with `import DataLoader from 'dataloader';` (a default import) when `dataloader` primarily exports a named `DataLoader` class in ESM, or an incorrect `require` statement.
fix
Use the correct named import for ESM: `import { DataLoader } from 'dataloader';`. For CommonJS, use `const DataLoader = require('dataloader');` to ensure you're getting the constructor.
Invariant Violation: DataLoader must be constructed with a function which accepts an Array of keys and returns a Promise which resolves to an Array of the same length as the Array of keys.
The batch loading function passed to the `DataLoader` constructor returned an array with a different number of elements than the input `keys` array, or did not return a Promise resolving to such an array.
fix
Review your batch loading function to ensure it always returns a Promise that resolves to an array whose length is exactly equal to the `keys` array it received, and that each element corresponds to the respective key in order (either a value or an `Error` object).
Upgrade
Version history
2.0latest on npm
Audit
Dependencies

No dependency data recorded yet.

Agent activity
19 hits · last 30 days
node
16
OpenAI (training)
1
Resources
dataloader — npm install dataloader · libregistry