Atomic updates
Update counters and flags atomically in the database.
Atomic update envelopes read and write a column in one database statement. They are useful for balances, counters, retry counts, and flags that may be changed concurrently.
const account = await client.accounts.update({
where: { id },
data: {
balance: { increment: 100 },
loginCount: { increment: 1 },
retries: { decrement: 1 },
locked: { toggle: true },
},
});Operators
Numeric columns accept increment, decrement, multiply, divide, and set. Boolean columns accept toggle: true. Direct values remain valid.
Several numeric operators may be combined. Better Drizzle always applies them in this order: set, increment, decrement, multiply, then divide.
await client.accounts.update({
where: { id },
data: { balance: { set: 10, increment: 4, multiply: 3 } }, // 42
});divide: 0, non-finite operands, empty envelopes, and operators used on incompatible columns fail before SQL is sent. NULL and integer-division behavior remain those of the selected database.
Supported writes
Use atomic envelopes in update, updateMany, updateEach, upsert, and upsertMany. In updateEach, return the envelope from the column callback:
await client.accounts.updateEach({
by: accounts.id,
data: [{ id: 1, delta: 5 }],
update: { balance: (row) => ({ increment: row.delta }) },
});PostgreSQL, SQLite, and MySQL support atomic updates wherever the underlying write API is available. upsertMany itself remains unavailable on MySQL.
Hooks
After an atomic write, post-write hooks receive a readonly compiled object containing the final SET expressions. Plugins still receive the declarative envelope before execution.
const client = better(db, {
schema,
hooks: {
afterUpdate({ compiled }) {
console.log(compiled);
},
},
});