---
title: "Transactions"
description: "Transactions in Tauri SQLite ORM."
canonical_url: "https://docs.ctrl-neo.dev/en/tauri-sqlite-orm/advanced-topics/transactions"
---
# Transactions

> Transactions in Tauri SQLite ORM.

A transaction is a sequence of operations performed as a single logical unit of work. The key property of a transaction is **atomicity**: either all of its operations succeed and are saved (`COMMIT`), or none of them are (`ROLLBACK`). This guarantees that your database is never left in a partially updated, inconsistent state.

This is essential for operations like transferring funds between two accounts, where you must both debit one account and credit another. If the credit operation fails, the debit must be undone.

## The `db.transaction()` Method

The ORM provides a `db.transaction()` method that makes managing transactions straightforward and safe. It takes a single argument: an `async` callback function.

```typescript
try {
  const result = await db.transaction(async (tx) => {
    // All your database logic goes here
    // ...
    return 'Success!';
  });
  console.log('Transaction committed:', result);
} catch (error) {
  console.error('Transaction rolled back:', error.message);
}
```

## How It Works

1. **BEGIN TRANSACTION**: Before your callback is executed, the ORM issues a `BEGIN TRANSACTION` command.
2. **The tx Object**: Your callback receives a special `tx` object. This object is a transactional version of the main `db` instance. **You must use tx for all queries inside the callback** (`tx.select`, `tx.update`, etc.) to ensure they are part of the transaction.
3. **COMMIT**: If your callback function runs to completion and returns a value (or a resolving `Promise`), the ORM automatically issues a `COMMIT` command, making all changes permanent.
4. **ROLLBACK**: If your callback function throws an error, the ORM catches it and issues a `ROLLBACK` command, undoing all changes made since the transaction began. The error is then re-thrown so you can handle it in a `catch` block.

## Example: A Safe Bank Transfer

Here is a practical example of transferring a balance between two user accounts.

```typescript
import { db } from '$lib/db';
import { users } from '$lib/db/schema';
import { eq, sql } from '@type32/tauri-sqlite-orm';

async function transferFunds(fromUserId: number, toUserId: number, amount: number) {
  if (amount <= 0) {
    throw new Error('Transfer amount must be positive.');
  }

  try {
    await db.transaction(async (tx) => {
      // 1. Debit the sender's account using decrement
      const result = await tx.update(users)
        .decrement('balance', amount)
        .where(eq(users._.columns.id, fromUserId))
        .execute();

      // If the sender didn't exist, rowsAffected will be 0
      if (result[0].rowsAffected === 0) {
        throw new Error(`Sender with ID ${fromUserId} not found.`);
      }

      // 2. Check if the sender has sufficient funds
      const sender = await tx.select(users, ['balance'])
        .where(eq(users._.columns.id, fromUserId))
        .get();
      
      if (sender.balance < 0) {
        // This error will trigger an automatic rollback!
        throw new Error('Insufficient funds.');
      }
      
      // 3. Credit the receiver's account using increment
      await tx.update(users)
        .increment('balance', amount)
        .where(eq(users._.columns.id, toUserId))
        .execute();
      
      console.log('Transfer steps completed within transaction.');
    });
    
    console.log('Transaction successful! Funds have been transferred.');

  } catch (error) {
    console.error(`Transaction failed: ${error.message}. All changes were rolled back.`);
  }
}
```


## Sitemap

See the full [sitemap](https://docs.ctrl-neo.dev/sitemap.md) for all pages.
