Skip to content
s3nd
Menu

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.

Packagess3nd

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