Amba
SDKsFeatures

Offline & resilience

Buffer writes when the device is offline and replay them in order on reconnect, apply optimistic updates with automatic rollback, and keep push delivery alive across token rotation.

Real apps go offline — a subway tunnel, an elevator, a flaky cafe network. The SDK has three opt-in capabilities so a lost connection never means lost data or a broken UI:

  1. Automatic offline queue — a write that fails because the device is offline is buffered to a durable on-device outbox and replayed in order the moment connectivity returns. Replays are idempotent, so a write that reached the server before the connection dropped is never applied twice.
  2. Optimistic updates — update your local view immediately, fire the real write, and roll the local change back automatically if the write fails.
  3. Push-token auto-rotation — re-register the device push token whenever the OS rotates it, so notifications keep arriving without the app noticing.

All three are opt-in and additive — existing code keeps its exact behavior until you turn them on.

Automatic offline queue

Call Amba.offline.enable() once after configure(). From then on, a collection write (insert / update / delete) or a track() that fails with a transport error is queued instead of throwing. On reconnect the queue drains in FIFO order.

import { Amba } from '@layers/amba-web';
 
await Amba.configure({ projectId: 'proj_...', clientKey: 'amba_ck_...' });
 
// Turn the outbox on. On the web it auto-flushes on the browser `online` event.
Amba.offline.enable();
 
// If the device is offline, this is buffered instead of throwing — your code
// keeps running. It replays automatically when the connection returns.
await Amba.collections.insert('todos', { title: 'Buy milk' });
 
// Inspect / drive the queue:
console.log(await Amba.offline.pendingCount()); // writes awaiting replay
await Amba.offline.flush(); // force a replay attempt now

Idempotent replay

Each queued collection write carries a stable idempotency key, sent on both the live attempt and every replay. If a write actually reached the server before the connection dropped, the replay is recognized as a duplicate and ignored — you never get a double-insert. This is automatic; you don't manage the keys.

Buffered events (track) are replayed preserving their original time, so an event keeps its real occurred_at instead of being stamped at flush. Events are attributed to the signed-in user at replay time and are at-least-once (a rare reconnect duplicate is acceptable for analytics). A replay that finds no valid session is retained — not dropped — so it lands once the session is back.

The queue API

MethodDescription
Amba.offline.enable()Turn the outbox on. Returns a disable function. Idempotent.
Amba.offline.isEnabled()Whether the outbox is currently on.
Amba.offline.pendingCount()Number of writes awaiting replay.
Amba.offline.pending()Snapshot the pending writes (a copy — safe to inspect).
Amba.offline.flush()Force a replay attempt now → { flushed, dropped, remaining }.
Amba.offline.clear()Drop every queued write without replaying.

A queued write that the server later rejects deterministically (e.g. a VALIDATION_ERROR it would fail every time) is dropped on replay rather than retried forever — it's reported in the dropped count (and a one-time warning) so one poisoned write can't wedge everything behind it. Transient connectivity failures, by contrast, keep the queue intact for the next attempt.

This is distinct from Amba.sync (manual change replay for explicit offline sync flows). Amba.offline is the automatic, fire-and-forget safety net for ordinary writes — turn it on and forget about it.

Optimistic updates

Show the change instantly, reconcile in the background, and roll back cleanly if the write fails. The collection surface has thin convenience helpers for the common single-write case:

await Amba.collections.optimisticInsert('todos', draft, {
  // 1. Update the local view now. Return a snapshot to restore on failure.
  apply: () => {
    const prev = todos;
    setTodos([...todos, draft]);
    return prev;
  },
  // 2. Undo if the write fails. Receives whatever `apply` returned.
  rollback: (prev) => setTodos(prev),
});
// optimisticUpdate(name, id, set, view) and optimisticDelete(name, id, view) too.

On success the helper resolves with the server's authoritative row. On failure it runs your rollback and rethrows the original error, so you can surface it.

Optimistic writes always hit the network directly — they are not diverted to the offline outbox even when Amba.offline is enabled. Optimistic UI needs an unambiguous outcome, so the write resolves only on a confirmed server write and rejects (running rollback) on any failure, including being offline. To buffer-and-replay instead, use the plain insert / update / delete with the outbox on.

For multi-step flows, the framework-agnostic withOptimistic helper takes any async mutateapply and rollback operate on whatever local view you hand them (a React state setter, a store, an in-memory cache). Give it a mutate that fails on error (a transaction, a function call, your own request) so a failure rejects and triggers rollback:

import { Amba, withOptimistic } from '@layers/amba-web';
 
await withOptimistic({
  apply: () => {
    const prev = cart;
    setCart([...cart, item]);
    return prev;
  },
  // A transaction commits atomically or throws — never silently queues.
  mutate: () =>
    Amba.collections.transaction([
      { collection: 'cart', op: 'insert', row: item },
      { collection: 'inventory', op: 'update', id: item.invId, set: { reserved: 1 } },
    ]),
  rollback: (prev) => setCart(prev),
});

Push-token auto-rotation

The OS can rotate a device's push token at any time (reinstall, restore, key roll). When it does, the old token stops delivering. Auto-rotation re-registers the fresh token through the idempotent registration path so notifications keep arriving.

// In your service worker, where `pushsubscriptionchange` fires.
import { Amba } from '@layers/amba-web';
 
await Amba.configure({ projectId: 'proj_...', clientKey: 'amba_ck_...' });
Amba.push.enableAutoRotation(); // re-registers the new subscription on rotation

Re-registration is idempotent, so re-registering the same device is a no-op server-side — it's always safe to leave auto-rotation on.

On this page