ExisJS

Data Loader (exisjs/dataloader)

This document provides the strict API signatures and TypeScript interfaces exported by the exisjs/dataloader module. For high-level guides on solving N+1 query problems, see the Data Loader Guide.


Dataloader<K, V, C> Class

The Dataloader class batches and caches requests to a backend data source.

import { Dataloader } from 'exisjs/dataloader'
class Dataloader<K, V, C = K> {  constructor(batchFn: BatchLoadFn<K, V>, options?: DataloaderOptions<K, C>)
  // Enqueues a key to be batched, returning a Promise that resolves to the value  load(key: K): Promise<V>
  // Loads multiple keys, returning an array of Promises  loadMany(keys: readonly K[]): Promise<V[]>
  // Clears the value at `key` from the cache  clear(key: K): this
  // Clears all values from the cache  clearAll(): this
  // Primes the cache with a predefined value or Error for a specific key  prime(key: K, value: V | Error): this}

BatchLoadFn<K, V>

The function responsible for loading the batched keys. It must return an array of values (or Errors) that are precisely the same length and in the exact same order as the provided keys array.

type BatchLoadFn<K, V> = (  keys: readonly K[]) => Promise<readonly (V | Error)[]>

DataloaderOptions<K, C>

interface DataloaderOptions<K, C = K> {  // Whether to cache resolved keys within this Dataloader instance. Default: true  cache?: boolean
  // The maximum number of keys to include in a single batch. Default: 1000  maxBatchSize?: number
  // A function to generate a unique cache key from the provided load key  cacheKeyFn?: (key: K) => C}

Context Registry (defineLoaders)

Because Dataloader instances are inherently stateful (they cache data), they must not be shared across different HTTP requests, or else users might read each other's cached data.

The defineLoaders function securely isolates Dataloader instances per-request by binding them to the ExisJS asynchronous context.

import { defineLoaders } from 'exisjs/dataloader'
function defineLoaders<  T extends Record<string, () => Dataloader<any, any>>>(factories: T): {  // Middleware to register onto your router/app  loaderMiddleware: (req, res, next) => void;    // Hook to retrieve the isolated loaders anywhere in the request lifecycle  getLoaders: () => { [K in keyof T]: ReturnType<T[K]> }}

Usage Signature

const { loaderMiddleware, getLoaders } = defineLoaders({  userLoader: () => new Dataloader(async (keys) => { /* ... */ })})