Offline-first answer sync for an exam app

Working notes on buffering answers in IndexedDB without letting the client become the authority.

06 November 2026Draft
  • indexeddb
  • offline
  • exams

/ This is a working draft. The structure is here; the details are still being verified and filled in.

The constraint

Exam rooms have unreliable networks. A participant in the middle of a listening section cannot be asked to reconnect before the next question, and an answer cannot be lost because a request timed out.

The tempting solution is to let the client own the exam state and sync everything at the end. That fails the moment the server needs to decide whether a section is still open.

The model

Three layers, with a clear owner for each:

Layer Owns Does not own
IndexedDB (Dexie) Draft answers, local queue Final submission state
Client app Rendering, navigation Scoring, timing authority
Server Timing, submission, scoring Offline availability

The write path:

  1. The answer is written to IndexedDB immediately.
  2. A sync job sends it with a client-generated identifier.
  3. The server upserts by that identifier, so retries are safe.
  4. The local entry is marked acknowledged.
  5. On reload, unacknowledged entries are replayed.

Why idempotency is the core

Networks fail in the ambiguous window: the request may have arrived and the response may have been lost. Without an idempotency key, a retry creates a duplicate answer and the participant’s score is wrong.

With one, the server can process the same write twice and end up in the same state. That property is more important than any particular sync interval.

Open questions to verify

  • How the client timer and the server timer are reconciled at section boundaries.
  • Whether a late answer arriving after submission is rejected or recorded for review.
  • How much of the queue needs to survive a browser restart versus a tab refresh.
  • What the operator sees when a participant has unsynced answers at the end of a session.

What I would write next

A worked example of the upsert endpoint and the Dexie table definition, plus a failure table: network drop, tab close, server restart, and clock skew.