Sync frontend
Enable sync on the client, providers, hooks, and local change APIs.
The frontend owns local CRUD, outbox tracking, checkpoint storage, and calling your backend’s pull/push endpoints through @melon-db/sync.
Enable sync on the database
const db = createDatabase({
schema: appSchema,
adapter,
migrations,
sync: {}, // required for getLocalChanges / applyRemoteChanges
});Optional config:
sync: { respectLocalOnly: true }, // skip collections with localOnly: trueMelonSyncProvider
Wrap the app inside MelonDbProvider:
import { MelonDbProvider, MelonSyncProvider } from '@melon-db/db-react';
import { DEFAULT_RETRY_POLICY } from '@melon-db/sync';
<MelonDbProvider db={db}>
<MelonSyncProvider
pullChanges={backend.pullChanges}
pushChanges={backend.pushChanges}
conflictPolicy="merge-by-field"
mergeProtectedFields={['updatedAt']}
retryPolicy={DEFAULT_RETRY_POLICY}
networkMonitor={myNetworkMonitor}
autoSyncOnReconnect
>
{children}
</MelonSyncProvider>
</MelonDbProvider>Implement pullChanges / pushChanges against your HTTP API — see Backend requirements.
Manual sync
const { sync, status, isSyncing, error, cancel } = useSync();
await sync();Or headless:
import { synchronize } from '@melon-db/sync';
await synchronize({
db,
pullChanges,
pushChanges,
conflictPolicy: 'server-wins',
retryPolicy: DEFAULT_RETRY_POLICY,
signal: abortController.signal,
});Protocol flow (client side)
| Step | API |
|---|---|
| 1. Pull | pullChanges({ lastPulledAt, schemaVersion, migration? }) |
| 2. Apply | db.applyRemoteChanges(changes, { conflictPolicy, ... }) |
| 3. Push | pushChanges({ changes: await db.getLocalChanges(), lastPulledAt }) |
| 4. Ack | db.markLocalChangesPushed() |
| 5. Checkpoint | store timestamp + schemaVersion from pull result |
Checkpoints persist in adapter meta when supported; otherwise in-memory until restart.
Conflict policies
Pass to MelonSyncProvider, synchronize, or applyRemoteChanges:
| Policy | Behavior |
|---|---|
server-wins | Remote replaces local on collision (default) |
client-wins | Keep local row |
skip-existing | Skip remote create when id exists |
last-write-wins | Compare sync timestamp field |
merge-by-field | Overlay pending local field patches onto remote |
custom | Your conflictResolver per record |
See Sync overview for merge-by-field and custom resolver examples.
Local-only collections
Mark schema collections with localOnly: true to exclude them from getLocalChanges when respectLocalOnly is true (default).
Status and network
useSyncStatus() exposes idle → pulling → pushing → complete, plus retrying, paused (offline), and failed.
Provide a NetworkMonitor so sync pauses offline and autoSyncOnReconnect can resume.
What you implement vs what Melon-db provides
| You implement | Melon-db provides |
|---|---|
| HTTP (or other transport) to your backend | Outbox, checkpoints, synchronize() orchestration |
pullChanges / pushChanges functions | getLocalChanges, applyRemoteChanges, conflict engines |
| Auth headers, tenancy, business rules | React hooks, retry policy, cancellation |
Related
- Backend requirements
- Data types — serializable field kinds
- FAQ — limitations and troubleshooting
- Sync playground
