Mutation queue

The mutation queue is the reliability core of offline write behavior. When a write cannot be sent immediately, FFDB stores it and retries during sync so user actions are not lost.

Why this matters

Without queueing, offline writes fail and users have to repeat work. Queueing solves this by separating local acceptance from remote delivery.

  • Name
    Offline-safe writes
    Description

    Users can keep working while disconnected.

  • Name
    Deterministic retry path
    Description

    Queued mutations are retried during push and removed only after success.

  • Name
    Operational visibility
    Description

    pendingMutations and push result counts make delivery state observable.

type SyncStatus = {
  isSyncing: boolean
  isOnline: boolean
  lastSyncedAt: number | null
  pendingMutations: number
  error: Error | null
}

type MutationEntry = {
  id: number
  sql: string
  params: string
  created_at: number
}

Push and full sync return explicit queue results:

type PushResult = {
  pushed: number
  failed: number
}

End-to-end queue flow

import { createClient } from 'ffdb-client'
import type { Database } from './ffdb.types'

const { db, sync } = await createClient<Database>({
  config: { apiUrl: import.meta.env.VITE_FFDB_API_URL },
  offline: {
    adapter: offlineAdapter,
    syncOnReconnect: true,
  },
})

// Write while offline or unstable network.
await db
  .updateTable('task')
  .set({ completed: 1 })
  .where('id', '=', taskId)
  .execute()

if (sync) {
  console.log('pending before push', sync.status.pendingMutations)

  const result = await sync.push()
  console.log(result) // { pushed, failed }

  console.log('pending after push', sync.status.pendingMutations)
}

Expected queue behavior

  1. Local write is accepted first.
  2. Mutation enters queue if remote delivery is not immediately available.
  3. sync.push() replays queued entries in order.
  4. Successfully delivered entries are removed from queue.
  5. Failed entries remain queued for future retries.

Failure handling patterns

  • Name
    Expose pending count in UI
    Description

    Show users when writes are waiting to sync.

  • Name
    React to failed counts
    Description

    If failed > 0, notify the user and offer manual retry.

  • Name
    Use reconnect triggers
    Description

    Pair queueing with syncOnReconnect so recovery is automatic.

  • Name
    Use waitForIdle for critical flows
    Description

    Wait for queue drain before completion screens that require remote confirmation.

Practical tradeoffs

  1. Queueing improves reliability but can delay remote visibility.
  2. Aggressive auto-sync improves freshness but increases network and battery usage.
  3. Manual retry controls improve transparency in high-latency environments.

Next pages

  1. Conflict behavior
  2. React
  3. Next.js

Was this page helpful?