Melon-dbMelon-db
Sync

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: true

MelonSyncProvider

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)

StepAPI
1. PullpullChanges({ lastPulledAt, schemaVersion, migration? })
2. Applydb.applyRemoteChanges(changes, { conflictPolicy, ... })
3. PushpushChanges({ changes: await db.getLocalChanges(), lastPulledAt })
4. Ackdb.markLocalChangesPushed()
5. Checkpointstore 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:

PolicyBehavior
server-winsRemote replaces local on collision (default)
client-winsKeep local row
skip-existingSkip remote create when id exists
last-write-winsCompare sync timestamp field
merge-by-fieldOverlay pending local field patches onto remote
customYour 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 implementMelon-db provides
HTTP (or other transport) to your backendOutbox, checkpoints, synchronize() orchestration
pullChanges / pushChanges functionsgetLocalChanges, applyRemoteChanges, conflict engines
Auth headers, tenancy, business rulesReact hooks, retry policy, cancellation