Replication Plugin
@routier/replication-plugin provides the HTTP and local-first replication building blocks for Routier. Use it when your app should read from local storage first, keep working offline, and synchronize with a remote API in the background.
When To Use It
Use the replication plugin when you want one of these patterns:
HttpDbPluginfor direct HTTP-backed collections and views.HttpSwrDbPluginfor stale-while-revalidate reads with background refresh.OptimisticUpdatesDbPluginfor memory-first reads layered on top of a persistent store.HttpSwrDbPlugin+OptimisticUpdatesDbPluginfor local-first SWR with optimistic updates.
For the full local-first background, see Local-First Apps. For the highest-performance composition, see HttpSwrDbPlugin with Optimistic Replication.
Installation
npm install @routier/replication-plugin
Install a local store plugin as well when you want persistent offline storage:
npm install @routier/dexie-plugin
Minimal Setup
import { DataStore } from "@routier/datastore";
import { DexiePlugin } from "@routier/dexie-plugin";
import { HttpSwrDbPlugin, OptimisticUpdatesDbPlugin } from "@routier/replication-plugin";
const cacheDb = new DexiePlugin("myapp_cache");
const unsyncedQueueDb = new DexiePlugin("myapp_unsynced");
const optimisticDb = new OptimisticUpdatesDbPlugin(cacheDb);
const plugin = new HttpSwrDbPlugin(optimisticDb, {
getUrl: (collectionName) => `https://api.example.com/${collectionName}`,
getHeaders: async () => ({ Authorization: `Bearer ${await getAccessToken()}` }),
maxAgeMs: 30_000,
unsyncedQueueStore: unsyncedQueueDb,
translateRemoteResponse(_schema, data) {
return (data as { data?: unknown[] }).data ?? [];
},
});
declare function getAccessToken(): Promise<string>;
export class AppDataStore extends DataStore {
constructor() {
super(plugin);
}
}Exported Plugins
| Export | Best for | Notes |
|---|---|---|
HttpDbPlugin | Server-backed reads and writes over HTTP | Sends query filters, sorts, skip, and take to your API. |
HttpSwrDbPlugin | Cache-first reads with background revalidation | Keeps local data fresh while the UI continues reading from cache. |
OptimisticUpdatesDbPlugin | Memory-first reads over a persistent source plugin | Hydrates memory from the wrapped source plugin and keeps reads extremely fast. |
Recommended Composition
For most production web apps, use this layering:
DexiePluginstores the durable local cache in IndexedDB.OptimisticUpdatesDbPluginmirrors that cache into memory for instant reads.HttpSwrDbPluginserves stale-while-revalidate reads and syncs writes to your API.
That gives you SWR semantics, optimistic local UX, and offline resilience without making every query wait on the network.
Key Options
HttpDbPlugin
HttpDbPlugin is the simplest remote plugin. Its main options are:
getUrl(collectionName): maps each Routier collection to an API endpoint.getHeaders(): injects auth or tenant headers per request.ignoreQueryForCollections: skips query serialization for collections the server always scopes itself.queryRetryBaseDelayMs,queryRetryMaxDelayMs,queryRetryMaxAttempts: control retry backoff for reads.translateRemoteResponse(schema, data): adapts your API payload to the array shape Routier expects.
HttpSwrDbPlugin
HttpSwrDbPlugin extends the HTTP options with local-first SWR behavior:
maxAgeMs: how long cached data is considered fresh before revalidation starts.bulkPersistRetryBaseDelayMs,bulkPersistRetryMaxDelayMs,bulkPersistRetryMaxAttempts: retry controls for writes.onAuthError(event): lets you trigger token refresh or sign-out on401and403.onRevalidateError(error, context): log or surface background refresh failures without breaking the current UI.unsyncedQueueStore: where pending writes are persisted until the server confirms them.
Operational Notes
translateRemoteResponseis usually required when your API returns{ data: [...] }or another wrapped payload.ignoreQueryForCollectionsis useful for server-owned scopes such as"users"or tenant-bound collections.unsyncedQueueStorepersists a reserved collection named_routier_unsynced; keep that store isolated from your main cache when your backend has store-specific constraints.- Override
formatRequestBody(...)onHttpSwrDbPluginwhen you need to strip client-only fields or match an existing API contract.