id, timestamps and defaults) without another read. Every write is checked against the table’s create, update or delete rule and against the columns’ rules.
Create
id, created_at and updated_at. If the table has an owner field, it is filled with the caller’s id.
Update some fields
update changes only the fields you send. Sending null clears an optional field. A required field (one that isn’t marked nullable) can’t be set to null: that’s a ValidationError.
Fields your rule depends on can’t be changed
If your table’s update rule reads a field, for example “only the owner may update” readsowner_id, then that field can’t be changed by your app’s users: the update is refused with PermissionError, naming the field. This stops someone handing a record to another person, or moving it into a team they’re not in. Change those fields from your server with a secret key, or from a flow.
Replace
replace sends the record with PUT. The server treats it exactly like update: it changes the fields you send and leaves the others as they are. It does not clear fields you leave out.
null (if it’s optional). Prefer update; replace exists for code that thinks in whole records.
Delete
NotFoundError. If the delete rule refuses, a signed-in person gets PermissionError and an anonymous caller gets NotFoundError. There’s no recycle bin: for “undo”, use a soft delete.
Validation errors
The server checks required columns, types and constraints before your rules run, and tells you which column is wrong:fieldError("email") under each input. Define the rules once, in Columns and types, and they apply to every client.
A taken unique value (a duplicate username) is a ConflictError:
What each write needs
Optimistic updates
Update the screen first, confirm with the server, roll back on failure:Avoid duplicate creates
A double-click or a retry can create two records. Two defences:- A unique column on a natural key (
order_number, or an idempotency key you generate on the client), so the second attempt is aConflictErroryou can treat as success. - Disable the button while the request is in flight.
POST after a network drop by itself, to avoid exactly this.
Concurrent edits
Two people editing the same record: the last write wins. To detect it, send theupdated_at you read and check it on the server (a route), or make fields independent so edits don’t collide.
Realtime echo
If the table broadcasts changes, you receive your own write back as a message. Merge byid so you don’t add it twice (Live records).
Bulk writes
Many at once.
Access
Why a write is refused.