API reference

yatta/batcher

DataLoader batching, so fifty concurrent calls are one query.

7 exported symbols and 13 members, read from src/types/batcher.ts.

Construct

batchBy

function

A loader over rows that need a key to join on.

For when the batch function returns rows in whatever order the database gave them rather than one per requested key — a join, an aggregation, anything with a `WHERE id IN (...)`.

batchBy<K, V>(batch: (keys: readonly K[]) => Promise<readonly V[]>, keyOf: (value: V) => K, options?: LoaderOptions): DataLoader<K, V>

hasLoaderScope

function

Whether this code is inside a loader scope.

hasLoaderScope(): boolean

loaderFor

function

A loader scoped to the current request.

The same name returns the same loader within one scope, so two components asking for the same key share one query and one cached value. Outside a scope a loader is created per call and behaves correctly but shares nothing — which is right for a script and means a server route must remember to wrap itself.

loaderFor<K, V>(name: string, batch: BatchFn<K, V>, options?: LoaderOptions): DataLoader<K, V>

withLoaders

function

Runs `fn` with a fresh set of loaders, discarded afterwards.

This is what makes batching safe on a server. A loader cached per user would be a cache of one user's data inside another user's request; cached on the app it would be shared by every concurrent request. Scoped to the call, it lives exactly as long as the work does.

withLoaders<T>(fn: () => T): T
tsts
// app/api/[[...path]]/route.tsexport const GET = withLoaders((req) => handle(req));

DataLoader

class

Batches and caches keys into single calls to `batch`.

```ts const loader = new DataLoader(async (ids: readonly string[]) => { const rows = await db.users.findMany({ where: { id: { in: [...ids] } } }); return new Map(rows.map((row) => [row.id, row])); }); // Three concurrent calls: one query, not three. const [a, b, c] = await Promise.all(ids.map((id) => loader.load(id))); ```

tsts
const loader = new DataLoader(async (ids: readonly string[]) => {  const rows = await db.users.findMany({ where: { id: { in: [...ids] } } });  return new Map(rows.map((row) => [row.id, row]));}); // Three concurrent calls: one query, not three.const [a, b, c] = await Promise.all(ids.map((id) => loader.load(id)));

10 members

  • cacheproperty
    cache:
  • queueproperty
    queue: K[]
  • queuedproperty
    queued:
  • timerproperty
    timer: ReturnType<typeof setTimeout> | undefined
  • load
    load(key: K): Promise<V>

    Loads one key.

    Two calls for the same key in the same window share one request — the second gets the first's promise rather than queueing a duplicate key.

  • schedule
    schedule(): void

    Resolves on the next tick. Batching is not useful for a single call.

  • flush
    flush(): Promise<void>

    Runs the batch. Public so a caller can force it at a known boundary.

  • settle
    settle(key: K, value: V | undefined, error: Error | undefined): void
  • clear
    clear(key?: K): void

    Empties the cache. The batch function is unchanged.

  • has
    has(key: K): boolean

    Whether a key already has a settled or in-flight value.

Types

LoaderOptions

interface
interface LoaderOptions

3 members

  • cacheproperty
    cache?: boolean

    Whether to cache results per key.

    On by default. Off for anything that changes often enough that a stale value is worse than a second query — a stock price, a queue depth.

  • maxBatchMsproperty
    maxBatchMs?: number

    Window in which calls are collected, in milliseconds.

    Defaults to 0, which collects everything queued in the current tick and flushes on the microtask. Raising it batches a burst across several ticks at the cost of adding that much latency to the first call in the window.

  • onMissingproperty
    onMissing?: (key: unknown) => Error

    Reported when `batch` resolves for fewer keys than it was given.

    The default throws, because a missing key means the caller's `load()` rejects and the failure surfaces far from the batch that lost it.

BatchFn

type

Fetches many keys at once. Receives the keys collected in one tick.

type BatchFn<K, V> = (keys: readonly K[]) => Promise<ReadonlyMap<K, V>> | Promise<V[]>
Tip
Most of the types above are inferred. You rarely import AuthConfig or JobPayload — declaring your schema once is enough for the rest to follow. See Typed keys.