Use case
Continuous backup
One snapshot per account, rewritten as the local database changes, with conditional writes so two devices cannot silently clobber each other.
const current = await store.getSnapshot(`user-${userId}`)
try {
await store.putSnapshot(`user-${userId}`, merged, { ifMatch: current?.etag })
} catch (error) {
if (isS3ndError(error) && error.code === 'PRECONDITION_FAILED') {
// Another device won. Read again, merge again.
}
throw error
}The situation
What is actually going on.
The problem
Once the app has accounts, a transfer code is the wrong shape. There is a session, so the server already knows who is asking. What the user wants is for their data to survive losing the laptop, without doing anything.
What s3nd does about it
Key the snapshot by user id instead of by a code, and rewrite it whenever the local database changes. Pass the ETag you last read as ifMatch: a second device that wrote in the meantime makes the write fail with PRECONDITION_FAILED instead of silently discarding its work, and your app reads again and merges.
Worth watching
The things that are easy to get wrong.
A different lifecycle rule
Backups must not expire. Keep them under their own prefix so the rule that cleans up transfers cannot reach them.
Debounce the writes
A snapshot per keystroke is a bill. Write on a timer, on visibility change, and before unload.
Merging is yours
s3nd tells you that you lost the race. Deciding what a merge means for your data is application code, which is why it is not hidden.
Related
Other shapes of the same primitive.
Put a file. Hand over the code.
Point it at the bucket you already pay for. Nothing to deploy, nothing to sign up for, nothing in the middle.
npm install -g @s3nd/clis3nd init --provider r2 --bucket drops3nd put ./anything.zip