TanStack DB synchronization
High-level helper
Section titled “High-level helper”createCollectionSyncHelpers keeps the three sides of one method together:
- Optimistic: update the local collection immediately.
- Outbound: call the Durable Object RPC method.
- Inbound: reconcile events received from the server.
import { createCollectionSyncHelpers, createDOTransport,} from '@repo/client-side-do/client';
const transport = createDOTransport<InventoryRpc>({ baseUrl: (id) => `wss://api.example.com/inventory/${id}`,});
const subscription = transport.subscriptions('inventory-1');
const sync = createCollectionSyncHelpers<InventoryRpc, EntryRecord>({ collection: entriesCollection, resolveSubscription: () => subscription,});
const { mutate, buildSubscriptionHandlers } = sync .method('updateEntry', { optimistic: ({ collection, args, metadata }) => { const [entry] = args; collection.update( entry.entryId, (draft) => Object.assign(draft, entry), { metadata } ); }, inbound: ({ collection, payload }) => { const entry = payload.result; if (!entry) return; collection.update(entry.entryId, (draft) => { Object.assign(draft, entry); }); }, }) .done();
subscription.setHandlers(buildSubscriptionHandlers(() => subscription));
await mutate.updateEntry?.({ entryId: 'entry-1', inventoryId: 'inventory-1', name: 'Updated',});Use buildSubscriptionHandlers when constructing the managed subscription so inbound events route to the matching method handler.
Optimistic metadata
Section titled “Optimistic metadata”createOptimisticActionMetadata creates typed metadata describing the method and its arguments. Collection callbacks can use it to choose an outbound operation without inventing a domain-specific metadata shape for every collection.
Bootstrap writes should carry source metadata and skip outbound RPC. Otherwise, loading a server snapshot into the local collection can send every loaded row back to the Durable Object.
Low-level builder
Section titled “Low-level builder”createTanstackCollectionFromClient remains available when you need explicit mutation and subscription handlers:
const { mutationHandlers, buildSubscriptionHandlers } = createTanstackCollectionFromClient<InventoryRpc, EntryRecord>({ collection: entriesCollection, resolveSubscription: ({ record }) => subscriptionsById.get(record.inventoryId) ?? null, }) .method('removeEntry', { outbound: ({ client, args }) => client.removeEntry(...args), inbound: ({ collection, payload }) => { collection.delete(payload.args[0].entryId); }, }) .done();Use the low-level builder for unusual routing or collection semantics. Prefer the high-level helper for new CRUD-style integrations.