MA10: Capstone: offline, usable and ready to release for KhetNote
Project 1: offline-first saving and sync
🎯You'll be able to
Complete Project 1: visits that save on the phone first, an outbox that survives restarts, and a sync engine that copes with lost answers, slow servers and conflicting edits.
🏠The everyday picture
A postman in a hill district writes each delivery in a notebook, even where there's no signal. Back in town, the notes go to the office one by one, each with its own number, so nothing is entered twice and nothing is lost if the bus is late.
📘What it really is
-
Storage (
src/storage/store.js): visits and the outbox saved in AsyncStorage under one key, loaded again when the app opens. The starter keeps them only in memory, so a restart loses them. -
Saving offline: a save writes to the phone and adds a change to the outbox at once, without waiting for the network.
-
The engine (
src/sync/engine.js): sends outbox changes in order when connectivity returns, with anIdempotency-Keyper change so a resend is stored once (MA5). -
Timing: retries back off (
src/sync/backoff.js), a refused change (422) is set aside so it doesn't block the rest, and a request with no answer gives up after 10 seconds (src/net/api.js). -
Pulling:
GET /visits?since=with the last cursor, so only new server changes come down. -
Conflicts: a 409 answer brings the server's visit;
src/sync/merge.jskeeps the adviser's change, takes other fields from the server, and flags a field both sides changed. -
The design (
docs/sync-design.md): the sections fromdocs/FORMATS.md, so the next developer knows how sync works.
📝A worked example
Input: test 05 failing: "a change whose answer was lost is sent again, and the server keeps only one copy".
Steps:
- Read the test: the server stores the change, then the answer is lost, and the app sends it again.
- Check the engine: the starter sends no
Idempotency-Key, so the server can't tell the second send is the same change. - Give each outbox change a key when it's created, store it with the change, and send it as the header every time, then run
npm run test:offline.
Expected output: test 05 passes; all ten pass in a few seconds in the reference.
🌍Where it's used
- Field apps for health workers, inspectors and sales teams.
- Messaging apps that queue messages until there's signal.
- Payment apps that use idempotency keys so a retried payment is taken once.
📝Case scenario
A learner's sync passed tests 1 to 6, then failed test 7: with the server set to slow, the app waited forever and the outbox didn't
move on. Adding the 10-second timeout, and treating it as "try again later", fixed it. They added the case to their sync design.
🖱️Try it
Start the practice server with npm run server, set it to down with curl -X POST http://127.0.0.1:8095/_mode -d down, and write
what you expect the app to do with two saved visits. Then set it to up. Success: your expectation matches tests 02 and 04.
⚠️Common mistakes
- Waiting for the network before saving. Sign: test 02 fails. Fix: save to the phone first, sync afterwards.
- No idempotency key. Sign: duplicate visits on the server. Fix: one key per change, sent on every try.
- Last write wins. Sign: an adviser's change disappears after a conflict. Fix: merge by field and flag clashes.
🛠️For your project
Project 1's evidence: the output of npm run test:offline with 10 passing, docs/sync-design.md, and a short note on which cases you
tested by hand with the practice server.
✅Quick check
Why send the same idempotency key when a change is resent?
Show the answer
So the server can tell it's the same change and store it once.
What should the app do when a request gets no answer in 10 seconds?
Show the answer
Give up on that try and send the change again later, keeping it in the outbox.
