CacheDBProviderclass

new CacheDBProvider<I, T>(source: DBProvider<I, T>, cache: MemoryDBProvider<I, T> = new MemoryDBProvider<I, T>())
ParamType
sourceDBProvider<I, T>
Provider with a fully asynchronous interface for database access. required
cacheMemoryDBProvider<I, T>
In-memory provider to use as the cache (a fresh MemoryDBProvider by default). Defaults to new MemoryDBProvider<I, T>()
Return
CacheDBProvider<I, T>
Database provider that keeps a copy of asynchronous remote data in a local synchronous cache.
PropertyType
.memoryMemoryDBProvider<I, T>
The in-memory provider holding the local synchronous cache of source data. required readonly

Database provider that keeps a copy of asynchronous remote data in a local synchronous cache.

  • Wraps a source provider and mirrors every read and write into an in-memory MemoryDBProvider, so subsequent reads can be served synchronously and live subscriptions stay seeded.
  • Reads fetch from source, then refresh the cache; writes hit source, then mirror the change into the cache.
  • Fetch-first item writes: updateItem() and deleteItem() fetch the item first (caching it) and skip the source write when it doesn't exist. Query writes are inherited two-step, resolving through this provider's own getQuery() — so the matched items are cached, and each per-item write mirrors exactly. The fetch and the writes are separate steps, so wrap them in transact() when they must be atomic.
  • Transactions run on source via transact() with a transaction-scoped mirror — only a committed transaction's writes reach the cache.
  • Discover the cache from a wrapping layer with getSource(CacheDBProvider, provider) to seed stores from .memory.

A wrapping provider that keeps an in-memory mirror in sync with a remote source. CacheDBProvider extends ThroughDBProvider, holds a MemoryDBProvider, and populates it as data is read and written, so subsequent reads are synchronous — the basis of synchronous first renders in the React integration.

The constructor takes the source provider and an optional MemoryDBProvider to use as the mirror (one is created by default).

Usage

ts
import { CacheDBProvider, ValidationDBProvider, MemoryDBProvider } from "shelving/db";

const provider = new CacheDBProvider(
  new ValidationDBProvider(new MemoryDBProvider())
);

await provider.getItem(POSTS, "abc"); // fetches from source, populates the mirror
await provider.getItem(POSTS, "abc"); // served synchronously from the mirror

DBCache finds the CacheDBProvider in a chain automatically and reuses its mirror to seed reactive stores.

Fetch-first writes

Writes that depend on existing data fetch before they write, so every write leaves the touched items cached and the cache mirrors exactly what changed:

  • CacheDBProvider.updateItem() and CacheDBProvider.deleteItem() fetch the item first (caching it) and skip the source write when it doesn't exist.
  • Query writes (setQuery, updateQuery, deleteQuery) are inherited two-step and resolve through the cache's own getQuery(), so the matched items are fetched and cached first, then each per-item write mirrors exactly — never a query replayed against the cache's subset.

The fetch and the writes are separate operations against source, so a concurrent change can slip between them — wrap the call in transact() when that matters, because inside a transaction both steps run in the source transaction and are atomic.

Transactions

CacheDBProvider.transact() runs the transaction on source and hands the callback this cache over the source's transaction, backed by a transaction-scoped mirror — so reads, fetch-first writes, and query resolution behave exactly as they do outside a transaction. The callback's operations are recorded with RecordingDBProvider, and once the source commits, the recorded writes are committed into the real cache with RecordingDBProvider.replayWrites().

Uncommitted data never touches the cache — a thrown callback commits nothing, and if the backend retries the callback only the committed attempt's writes are mirrored. Update writes commit as deltas, so they refresh cached items and skip uncached ones; an item only read inside a transaction stays uncached until its next read. (The recording also holds the reads, so a future refinement could commit those with RecordingDBProvider.replay().)