Course contentsShow
← Node.js
Lesson 1114 of 1,98010. Real-time Communication and WebsocketsFree lesson

Handling Offline-First Persistence Patterns

You will design a state reconciliation loop that synchronizes local changes after a period of offline status.

In this lesson, you will learn how to design a state reconciliation loop that brings your local client data back into alignment with the server after your application regains connectivity.

The Reconciliation Challenge

When a client goes offline, it may continue to mutate local data (optimistic updates). Meanwhile, the server state continues to evolve. When the connection is re-established, simply overwriting the server state with local data (or vice versa) results in lost work or race conditions. A reconciliation loop acts as the bridge that intelligently merges these divergent timelines.

Building the Loop

To reconcile, we treat the local state as a queue of "pending mutations" rather than a final source of truth. Each mutation should be assigned a sequenceID (as discussed in our previous lessons on sequencing). When the WebSocket reconnects, the client initiates a sync process:

  1. Identify Divergence: The client sends the lastSeenSequenceID to the server.
  2. Server Replay: The server sends all missed delta updates that occurred after that sequence ID.
  3. Local Replay: The client applies its pending local changes (those not yet acknowledged by the server) on top of the server’s latest state.
async function reconcileState(localQueue, lastConfirmedId) {
  // 1. Fetch missing updates from server
  const missedUpdates = await socket.emit('get-missed-updates', { lastConfirmedId });
  
  // 2. Apply missed updates to local state
  missedUpdates.forEach(update => applyToLocalState(update));
  
  // 3. Re-apply pending local changes in chronological order
  localQueue.sort((a, b) => a.sequenceID - b.sequenceID);
  localQueue.forEach(mutation => {
    // Re-run the local mutation logic over the updated state
    applyToLocalState(mutation);
    socket.emit('sync-mutation', mutation);
  });
}

Managing Conflicts

If a local mutation conflicts with a server update during this replay, you must apply your conflict resolution strategy (like "Last Write Wins" or "Server Wins"). By ensuring your local queue remains immutable until confirmed, you maintain a predictable state that can be replayed safely during reconnection events.

Key Takeaway: A successful reconciliation loop treats local offline changes as a ordered sequence of mutations that must be replayed against the server’s latest state upon reconnection.