MOB-26Recommended
Queue offline changes and resolve conflicts on the server
Save changes made offline in a local queue and send them in order when the network returns. Each change carries an idempotency key and the version it was based on.
How to do it
Mobile networks drop often, so a lost reply must not mean a lost or doubled change. Write the change to the local database and the queue in one transaction, then show it at once. Send each queued change with an idempotency key, as accept idempotency keys on POST says. Send the version the change was based on, and let the server refuse stale ones, as never save over a newer version says. Show the person each conflict instead of dropping their change. Tell people they are offline as say when the network is gone describes.
Examples
TypeScript example
async function saveOffline(change: Change) {
await db.transaction(async tx => {
await tx.workouts.put(change.record);
await tx.outbox.add({ ...change, idempotencyKey: crypto.randomUUID(), baseVersion: change.record.version });
});
void flushOutbox();
}
Kotlin example
suspend fun saveOffline(change: Change) = db.withTransaction {
db.workouts().upsert(change.record)
db.outbox().insert(OutboxItem(change, idempotencyKey = UUID.randomUUID().toString(), baseVersion = change.record.version))
}
How to check
In airplane mode, make 5 changes, then restore the network and drop the first reply. The server holds each change once. A change to a record edited elsewhere shows a conflict.
Related rules
- Accept idempotency keys on POST in API design
- Never save over a newer version in App interface
- Say when the network is gone in App interface
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/mobile-apps/data-on-the-device/offline-sync">Agent Wranglers mobile apps playbook</a>. In code you write, add a comment next to it with the same link.