Offline-first apps
Keep reading, querying, listening and writing documents with no connection; everything syncs in order when it comes back.
Turn on offline mode once and documents keep working without a network: reads and queries answer from a local cache, live listeners keep firing, and writes are applied locally at once and queued. When the connection returns, the queue is sent in the original order. It works in browsers, React Native and Node.
import { SovariaSDK, indexedDbStorage } from '@sovaria/sdk'
const sov = new SovariaSDK({ projectUrl: 'https://<ref>.sovaria.in', anonKey: '<anon key>' })
const offline = sov.enableOffline({
storage: indexedDbStorage(), // survives reloads
onSyncError: (error, writes) => console.warn('refused by the server', error, writes),
})
await sov.collection('todos').add({ title: 'works on a plane', done: false }) // instant, even offline
const stop = await sov.collection('todos').where('done', '==', false).onSnapshot((snap) => {
render(snap.docs.map((d) => ({ ...d.data, saving: d.hasPendingWrites })))
})
await offline.pendingWrites() // what still has to reach the serverHow it behaves
| Online | Offline | |
|---|---|---|
| Read a document | From the server; the cache is refreshed | From the cache (fromCache); a document never read before fails with NETWORK_ERROR |
| Query and count | From the server; results are cached | Run on the cached documents of the collection with the same operators, ordering, cursors and limits |
| Live listeners | Pushed changes | Keep firing from the cache, including your own offline writes (hasPendingWrites); a listener started offline answers from the cache |
| Writes and batches | Committed at once | Applied to the cache at once (field transforms too: increment, arrayUnion, serverTimestamp…) and queued |
| Transactions | Run on the server | Fail with NETWORK_ERROR: their reads must be current, as in Firebase |
- When the connection is back (the browser's
onlineevent, the next successful request, or every 30 seconds) the queue is sent one commit at a time, oldest first, so the server sees the writes in the order you made them. - A queued write the server refuses — a collection rule, a row policy, a version precondition — is dropped, reported to
onSyncError, and the cache is put back to the server's version of those documents. - Access rules still apply: nothing in the queue skips them, and the cache only holds documents this user was allowed to read.
- Call
offline.clear()when a user signs out, so the next user of the device doesn't see their cache or queue.
Where the cache lives
| Storage | Use it for |
|---|---|
indexedDbStorage() | Browsers: survives reloads and restarts. |
keyValueStorage(AsyncStorage) | React Native, or any store with getItem / setItem / removeItem / getAllKeys. |
memoryStorage() | The default: tests, servers, or a cache that ends with the page. |
Something unclear or missing on this page?Tell us