guide

Conflicts

Stop two people booking the same 3pm slot, taking the same username or buying the last item in stock. Each protection is a precondition: an option on a call you already make.

A 409 conflict means someone else got there first. Catch it and tell the person the slot, stock or edit was taken. Do not hide the conflict behind an automatic retry.

Uniqueness with keyed creates

Derive the key from the thing that must be unique. The second writer gets a 409; your own retry returns your record (existing: true); deleting the record frees the key. Keys are 1–120 characters of letters, numbers and : _ . @ / -.

await bookings.create({ who: email }, { key: "slot:2026-07-15T15:00" })

Limited stock: N units anyone can buy

Claim units with keyed creates, and on conflict try the next unit. When every unit is taken, the item is sold out. This holds under every safety rule, because no rule lets a person write another person's records.

await orders.create({ item: 42 }, { key: "unit:item42:1" })  // then :2 … :N

Atomic counters

Atomic ops run against the current state on the server. An op that would pass the floor or ceiling returns 409 and leaves the record untouched.

await products.update(id, { stock: { decrement: 1, floor: 0 } })

An object in a patch is an atomic op only when its keys are increment or decrement (plus an optional floor or ceiling) and every value is a number. Anything else is stored as plain data.

Shared editing with versions

Pass back the version you read. A stale save gets a 409 and leaves the other person's edit in place. Re-read, reapply your change, and retry.

await pages.update(id, { body }, { ifVersion: page.version })

One per person, limited supply

A booking that must be unique and capped per person needs two keyed creates: claim the resource first, then the per-person cap. If the second 409s, delete the first, which frees its key:

const slot = await bookings.create({ when }, { key: "slot:" + when })
try {
  await quotas.create({ ref: slot.id }, { key: "user-day:" + user.userId + ":" + day })
} catch (e) {
  if (e.code === "conflict") { await bookings.delete(slot.id) }  // give the slot back
  throw e
}

Don't check-then-create, and don't compute counters in the client. Both break when two people act at once.

What happens if…

If…What happensWhat you do
Two people create with the same keyThe first wins; the second gets 409 conflict. Keys are unique across the whole collection, whoever wrote the recordTell the second person it was taken
Your own create with a key is retriedYou get your existing record back with existing: true; nothing is created twiceNothing
A create without a key is retriedThe retry can make a second recordUse a key for anything that must happen once
The record holding a key is deletedThe key is free againNothing
A counter op would pass its floor or ceiling409 conflict; the record is untouched. A field that was never written counts from 0Tell the person it ran out
A save carries a version someone else already moved on from409 conflict; the other person's edit staysRe-read, reapply, and save again
Two apps use the same keyNo conflict; keys are per collection, inside one appNothing
Was this page helpful?

Last updated