DataLoader

A batching and caching layer that coalesces per-request data loads into a fraction of the backend calls.

Library
npm
v2.2.3
13,389stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
68/100Good
Development Activity64
Maintenance36
Community72
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
86/100Excellent
Architecture85
Code Quality88
Innovation92
Learning Curve80

DataLoader is a generic utility for an application’s data-fetching layer, providing a consistent API over any backend (SQL, key-value stores, REST or gRPC services) via automatic batching and per-request caching. Rather than replacing a database or cache, it sits in front of one: every .load(key) call within a single tick of the event loop is coalesced into a single call to a user-supplied batch function, and the resulting values are memoized for the lifetime of the DataLoader instance so repeated loads of the same key never hit the backend twice.

Originally a port of Facebook’s internal “Loader” abstraction that underpinned its early GraphQL server, DataLoader is the reference implementation the JavaScript GraphQL ecosystem builds on to avoid the N+1 query problem, and it has been reimplemented in a dozen other languages. It ships with zero runtime dependencies, a small five-method API surface (load, loadMany, clear, clearAll, prime), and enough configuration (custom cache maps, cache key functions, batch schedulers, max batch size) to fit almost any backend shape without changing how calling code is written.

What You Get

  • Automatic request-scoped batching that coalesces all .load() calls issued within a single tick of the event loop into one call to your batch function
  • A built-in memoization cache (backed by Map by default, or any custom cache-like object) that eliminates redundant loads for the same key within a request
  • loadMany() for loading multiple keys at once, resolving per-key errors as values instead of rejecting the whole batch
  • prime() to seed the cache ahead of time (e.g. cross-populate a by-ID loader from a by-username loader), and clear()/clearAll() for invalidating stale entries after a mutation
  • Pluggable batchScheduleFn, cacheKeyFn, and cacheMap options for adapting the scheduling and caching strategy to non-default environments (browsers, long-lived processes, object keys)
  • Zero runtime dependencies and both Flow and hand-written TypeScript type definitions shipped in the package

Common Use Cases

  • Resolving GraphQL fields without N+1 database queries, by giving each field resolver a shared per-request loader instead of querying the database directly
  • Deduplicating and batching calls to a REST or gRPC backend service across unrelated parts of a request-handling pipeline
  • Providing a per-request cache in front of SQL, Redis, CouchDB, RethinkDB, or Google Datastore lookups (patterns documented in the project’s examples directory)
  • Loading the same entity by multiple keys (e.g. by ID and by username) while keeping both caches in sync via prime()
  • Invalidating cached reads after a mutation within the same request, using clear()/clearAll() to force a fresh load on subsequent calls

Under The Hood

Architecture The entire implementation lives in one ~500-line file (src/index.js) that exposes a single DataLoader class. Each instance tracks a current “batch” record (pending keys, resolve/reject callbacks, and cache-hit callbacks) that getCurrentBatch either reuses or creates; the first .load() call on a fresh batch schedules dispatch via a configurable batchScheduleFn, defaulting to enqueuePostPromiseJob, which chains a resolved Promise into process.nextTick (or setImmediate/setTimeout in non-Node environments) so the batch closes only after the current synchronous execution and any already-queued microtasks complete. dispatchBatch enforces the batch-function contract (must return a Promise resolving to an array-like of the same length as the input keys) and resolves or rejects each pending Promise by index, while resolveCacheHits replays cached results in the same tick so cached and freshly-loaded values settle together; failedDispatch centralizes cleanup for both synchronous throws and rejected batch promises, clearing the cache for every affected key and rejecting each callback. The Batch record is the one abstraction everything else depends on — changing its shape would ripple through getCurrentBatch, dispatchBatch, resolveCacheHits, and failedDispatch together.

Tech Stack Pure JavaScript with Flow (@flow strict) type annotations in the source, compiled through Babel (@babel/core, @babel/preset-env, @babel/preset-flow) into the published dist/ output, alongside a hand-maintained index.d.ts for TypeScript consumers. There are no runtime dependencies beyond the global ES6 Promise and Map. Releases are versioned and changelogged through Changesets and published to npm; CI (GitHub Actions) runs the full lint + flow-check + Jest suite across multiple Node versions via Yarn and reports coverage to Codecov.

Code Quality The test suite is extensive relative to the implementation size — well over a thousand lines across dedicated files for the core API, abusive/malformed-input scenarios, browser scheduling fallbacks, and unhandled-rejection behavior. Error handling is explicit throughout: constructor and option validators throw descriptive TypeErrors synchronously for misuse, and asynchronous batch failures are funneled through a single failedDispatch path so no request is left hanging. Naming is consistent (underscore-prefixed private fields, method names mirroring Promise.all semantics), and both a static type checker (Flow) and a separately maintained TypeScript declaration file back the public API, enforced together with ESLint and Prettier in CI.

API Design The public surface is deliberately small — five methods cover the whole batching and caching contract, and a DataLoader can be constructed with nothing more than a single batch function; every other behavior (batch size limits, custom scheduling, custom cache keys or cache backends) is opt-in with sensible defaults. Documentation is unusually thorough for a library this size, with a long-form README covering batching, caching, custom schedulers, custom cache implementations, and GraphQL integration, plus a dedicated examples directory walking through SQL, Redis, CouchDB, RethinkDB, Google Datastore, and Knex backends.

Used by 10 apps in this directory

TypeScript
99%
MIT

GraphQL Hive

Developer Tools · Devops · Monitoring

484

Open-source GraphQL schema registry and observability platform with breaking change detection, federation support, and CI/CD integration for teams of any size.

View details
89
Repo Health
81
Technical
69
Dependency
Built with
TypeScript99%
Updated 2 days ago
TypeScript
99%
MIT

KeystoneJS

CMS · Developer Tools

9,969

The superpowered headless CMS for developers built with GraphQL and React

View details
91
Repo Health
81
Technical
64
Dependency
Built with
TypeScript99%
Updated 5 days ago
TypeScript
88%
Apache 2.0

Medplum

Developer Tools · Databases · Authentication

2,657

An open-source, FHIR-native healthcare platform that gives developers a compliant backend, authentication, a React component library, and serverless bots to build clinical applications in weeks instead of years.

View details
93
Repo Health
90
Technical
72
Dependency
Built with
TypeScript88%
MDX10%
Updated yesterday
TypeScript
64%
Other

NocoDB

No Code Platforms · Databases · Low Code Platforms

64,861

Turn any SQL database into a collaborative no-code spreadsheet with automatic REST APIs and real-time views.

View details
90
Repo Health
77
Technical
62
Dependency
Built with
TypeScript64%
Vue31%
Updated 2 days ago
JavaScript
26%
AGPL 3.0

Omnivore

Knowledge Management · Bookmarks Archiving · Note Taking

16,228

Self-hosted read-it-later platform with highlights, newsletters, PDFs, and seamless Obsidian and Logseq integration.

View details
88
Repo Health
74
Technical
65
Dependency
Built with
JavaScript26%
TypeScript25%
HTML19%
Updated 2 days ago
TypeScript
96%
MIT

Payload CMS

Developer Tools · Blogging · CMS

44,600

The open-source, Next.js-native headless CMS that lives inside your /app folder and gives you a full TypeScript backend instantly.

View details
91
Repo Health
88
Technical
65
Dependency
Built with
TypeScript96%
Updated 3 days ago
TypeScript
97%
Other

Rocket.Chat

Team Chat

46,089

The secure, self-hosted team communications platform for organizations that cannot compromise on data sovereignty.

View details
96
Repo Health
79
Technical
66
Dependency
Built with
TypeScript97%
Updated yesterday
TypeScript
99%
MIT

Sanity

CMS

6,312

Open-source headless CMS with a fully customizable React Studio, real-time collaborative editing, structured content modeling, and GROQ query language

View details
92
Repo Health
90
Technical
66
Dependency
Built with
TypeScript99%
Updated yesterday
TypeScript
80%
Other

twenty

CRM

56,361

The open-source CRM you build, ship, and version like the rest of your stack — with customizable objects, AI agents, and a TypeScript SDK.

View details
92
Repo Health
82
Technical
64
Dependency
Built with
TypeScript80%
MDX17%
Updated yesterday

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search