Skip to main content

Transactions

Execute multiple database operations atomically within functions. All operations within a transaction either succeed together or fail together.

Overview

Transactions allow you to:

  • Execute multiple operations as a single atomic unit
  • Read your own uncommitted changes within the transaction
  • Rollback all changes if any operation fails
  • Choose isolation levels for concurrent access control

Basic Usage

Use beginTransaction() to start a transaction, then use the returned handle for all operations:

const tx = await beginTransaction();

try {
// All operations use the transaction handle
const order = await tx.post('orders', {
customerId: 'c123',
total: 99.99
});

await tx.post('order-items', {
orderId: order.data[0],
productId: 'p456',
quantity: 2
});

// Commit persists all changes
await tx.commit();
} catch (e) {
// Rollback discards all changes
await tx.rollback();
throw e;
}

Transaction Handle Methods

The transaction handle provides the same HTTP methods as the global context:

MethodDescription
tx.get(url)GET request within transaction
tx.post(url, data)POST request within transaction
tx.put(url, data)PUT request within transaction
tx.patch(url, data)PATCH request within transaction
tx.delete(url)DELETE request within transaction
tx.commit()Persist all changes
tx.rollback()Discard all changes

Reading Your Own Writes

Within a transaction, you can read data you've just written (even before commit):

const tx = await beginTransaction();

// Create a product
await tx.post('products', {
name: 'Widget',
price: 29.99,
stock: 100
});

// Read it back within the same transaction
const products = await tx.get('products?filter=name eq "Widget"');
log(products.data[0].stock); // 100

await tx.commit();

Error Handling

Always wrap transactions in try-catch to ensure proper cleanup:

const tx = await beginTransaction();

try {
await tx.post('accounts', {id: 'a1', balance: -100}); // Debit
await tx.patch('accounts/a2', {balance: 100}); // Credit

// Validate business rules
const account = await tx.get('accounts/a1');
if (account.data[0].balance < 0) {
throw new Error('Insufficient funds');
}

await tx.commit();
} catch (e) {
await tx.rollback();
logError('Transfer failed: ' + e.message);
throw e;
}

Use Cases

ScenarioDescription
Order processingCreate order and line items atomically
Inventory updatesDeduct stock and create order together
Account transfersDebit one account and credit another
Bulk operationsInsert multiple related records
Data migrationsUpdate related records consistently

Best Practices

  • Keep transactions short — Long transactions can block other operations
  • Always handle errors — Use try-catch and call rollback() on failure
  • Don't mix with global methods — Use only tx.* methods inside a transaction
  • Validate before commit — Check business rules within the transaction