Transactions
Execute multiple operations atomically with database transactions.
Basic Transactions
basic.ts
// Automatic transaction with callback
await db.transaction(async (trx) => {
// All queries use the transaction
const user = await trx.query(
cook`nocap:users drip:name,email fire:John,john@test.com flex:id`
);
await trx.query(
cook`nocap:profiles drip:user_id,bio fire:${user.id},New user`
);
// If any query fails, all changes are rolled back
});Manual Transactions
manual.ts
// Manual control over commit/rollback
const trx = await db.beginTransaction();
try {
await trx.query(cook`glow:accounts rizz:balance=balance-100 sus:id=1`);
await trx.query(cook`glow:accounts rizz:balance=balance+100 sus:id=2`);
// Commit if all successful
await trx.commit();
} catch (error) {
// Rollback on any error
await trx.rollback();
throw error;
}Nested Transactions (Savepoints)
savepoints.ts
await db.transaction(async (trx) => {
await trx.query(cook`nocap:orders drip:user_id,total fire:1,100`);
// Create a savepoint
await trx.transaction(async (nested) => {
await nested.query(cook`nocap:order_items drip:order_id,product_id fire:1,42`);
// This inner transaction can be rolled back independently
if (outOfStock) {
throw new Error('Out of stock');
// Only rolls back to savepoint, outer transaction continues
}
}).catch(() => {
// Handle nested transaction failure
console.log('Order item failed, continuing without it');
});
// Outer transaction still commits
});Isolation Levels
isolation.ts
// Set isolation level
await db.transaction(
async (trx) => {
// Queries here see a consistent snapshot
const balance = await trx.query(cook`main:accounts slay:balance sus:id=1`);
},
{ isolation: 'serializable' }
);
// Available levels:
// - 'read uncommitted'
// - 'read committed' (default)
// - 'repeatable read'
// - 'serializable'Retry Logic
retry.ts
// Automatic retry on serialization failures
await db.transaction(
async (trx) => {
const account = await trx.query(
cook`main:accounts slay:* sus:id=1`
);
await trx.query(
cook`glow:accounts rizz:balance=${account.balance - 100} sus:id=1`
);
},
{
isolation: 'serializable',
retry: {
times: 3,
delay: 100, // ms between retries
onRetry: (attempt, error) => {
console.log(`Retry ${attempt}: ${error.message}`);
}
}
}
);When to Use
- Money transfers between accounts
- Creating related records together
- Inventory updates with orders
- Any multi-step data changes
Best Practices
- Keep transactions short
- Don't do I/O inside transactions
- Use appropriate isolation levels
- Handle deadlocks with retries