Skip to main content

Transaction

The Transaction class provides ACID-compliant transaction support for performing multiple database operations atomically. Transactions ensure data consistency and allow you to roll back changes if an error occurs.

Creating a transaction

Transactions are created using db.beginTransaction():
const txn = db.beginTransaction();

try {
  // Perform operations
  txn.insert('users', { name: 'Alice', email: '[email protected]' });
  txn.insert('posts', { userId: 'alice123', content: 'Hello!' });
  
  // Commit the transaction
  txn.commit();
} catch (error) {
  // Roll back on error
  txn.rollback();
  throw error;
}

Transaction lifecycle

isActive()

Checks if the transaction is still active and can be used.
isActive(): boolean
active
boolean
true if transaction is active, false if committed or rolled back
Example
const txn = db.beginTransaction();
console.log(txn.isActive()); // true

txn.commit();
console.log(txn.isActive()); // false

commit()

Commits the transaction, making all changes permanent.
commit(): void
Example
const txn = db.beginTransaction();
txn.insert('users', { name: 'Alice', email: '[email protected]' });
txn.commit(); // Changes are now permanent

rollback()

Rolls back the transaction, discarding all changes.
rollback(): void
Example
const txn = db.beginTransaction();
try {
  txn.insert('users', { name: 'Alice', email: '[email protected]' });
  // Some operation that might fail
  throw new Error('Something went wrong');
} catch (error) {
  txn.rollback(); // Discard all changes
  console.log('Transaction rolled back');
}

CRUD operations

All CRUD operations in transactions are type-safe when you use TypeScript generics.

insert()

Inserts a document into a collection.
insert<T extends Document>(collectionName: string, doc: Omit<T, '_id'>): string
collectionName
string
required
Name of the collection
doc
Omit<T, '_id'>
required
Document to insert (without _id field, which will be auto-generated)
id
string
The auto-generated _id of the inserted document
Example
interface User extends Document {
  _id: string;
  name: string;
  email: string;
  age: number;
}

const txn = db.beginTransaction();
const userId = txn.insert<User>('users', {
  name: 'Alice',
  email: '[email protected]',
  age: 30
});

console.log(`Inserted user with ID: ${userId}`);
txn.commit();

findById()

Finds a document by its ID.
findById<T extends Document>(collectionName: string, id: string): T | null
collectionName
string
required
Name of the collection
id
string
required
Document ID to find
document
T | null
The document if found, null otherwise
Example
const txn = db.beginTransaction();
const user = txn.findById<User>('users', 'user123');

if (user) {
  console.log(`Found user: ${user.name}`);
}

txn.commit();

updateById()

Updates a document by its ID.
updateById<T extends Document>(collectionName: string, id: string, updates: Partial<T>): void
collectionName
string
required
Name of the collection
id
string
required
Document ID to update
updates
Partial<T>
required
Partial document with fields to update
Example
const txn = db.beginTransaction();
txn.updateById<User>('users', 'user123', {
  age: 31,
  email: '[email protected]'
});
txn.commit();

deleteById()

Deletes a document by its ID.
deleteById(collectionName: string, id: string): void
collectionName
string
required
Name of the collection
id
string
required
Document ID to delete
Example
const txn = db.beginTransaction();
txn.deleteById('users', 'user123');
txn.commit();

findAll()

Retrieves all documents from a collection.
findAll<T extends Document>(collectionName: string): T[]
collectionName
string
required
Name of the collection
documents
T[]
Array of all documents in the collection
Example
const txn = db.beginTransaction();
const allUsers = txn.findAll<User>('users');
console.log(`Total users: ${allUsers.length}`);
txn.commit();

count()

Counts all documents in a collection.
count(collectionName: string): number
collectionName
string
required
Name of the collection
count
number
Number of documents in the collection
Example
const txn = db.beginTransaction();
const userCount = txn.count('users');
console.log(`Total users: ${userCount}`);
txn.commit();

Collection management

createCollection()

Creates a new collection.
createCollection(collectionName: string): void
collectionName
string
required
Name of the collection to create
Example
const txn = db.beginTransaction();
txn.createCollection('posts');
txn.createCollection('comments');
txn.commit();

dropCollection()

Deletes a collection and all its documents.
dropCollection(collectionName: string): void
collectionName
string
required
Name of the collection to drop
Example
const txn = db.beginTransaction();
txn.dropCollection('temp_data');
txn.commit();

renameCollection()

Renames a collection.
renameCollection(oldName: string, newName: string): void
oldName
string
required
Current name of the collection
newName
string
required
New name for the collection
Example
const txn = db.beginTransaction();
txn.renameCollection('users', 'accounts');
txn.commit();

Advanced transaction patterns

Multi-collection atomic operations

Transactions can span multiple collections, ensuring atomicity across all operations:
interface User extends Document {
  _id: string;
  name: string;
  email: string;
}

interface Post extends Document {
  _id: string;
  userId: string;
  content: string;
  createdAt: number;
}

const txn = db.beginTransaction();

try {
  // Insert user
  const userId = txn.insert<User>('users', {
    name: 'Alice',
    email: '[email protected]'
  });
  
  // Insert posts for the user
  txn.insert<Post>('posts', {
    userId,
    content: 'My first post!',
    createdAt: Date.now()
  });
  
  txn.insert<Post>('posts', {
    userId,
    content: 'My second post!',
    createdAt: Date.now()
  });
  
  // All operations succeed or fail together
  txn.commit();
} catch (error) {
  txn.rollback();
  console.error('Failed to create user and posts:', error);
}

Conditional updates

You can perform read-modify-write operations safely within a transaction:
const txn = db.beginTransaction();

try {
  const user = txn.findById<User>('users', 'user123');
  
  if (!user) {
    throw new Error('User not found');
  }
  
  // Only update if condition is met
  if (user.age < 18) {
    throw new Error('User must be 18 or older');
  }
  
  txn.updateById<User>('users', 'user123', {
    verified: true
  });
  
  txn.commit();
} catch (error) {
  txn.rollback();
  throw error;
}

Data migration

Transactions are ideal for data migrations:
const txn = db.beginTransaction();

try {
  // Read all documents
  const users = txn.findAll<User>('users');
  
  // Migrate each document
  for (const user of users) {
    // Add new field
    txn.updateById<User>('users', user._id, {
      status: 'active',
      updatedAt: Date.now()
    });
  }
  
  txn.commit();
  console.log(`Migrated ${users.length} users`);
} catch (error) {
  txn.rollback();
  console.error('Migration failed:', error);
}

Transaction retry configuration

Transactions automatically retry on conflicts. Configure retry behavior using database-level settings:
import { Database } from '@sohzm/jasonisnthappy';

const db = Database.open('./my-database.db');

// Configure transaction retry behavior
db.setTransactionConfig({
  maxRetries: 5,              // Retry up to 5 times
  retryBackoffBaseMs: 10,     // Start with 10ms delay
  maxRetryBackoffMs: 1000     // Max 1 second between retries
});

// Transactions will now use this configuration
const txn = db.beginTransaction();

Best practices

Keep transactions short

Transactions hold locks, so keep them as short as possible:
// Good: Short transaction
const txn = db.beginTransaction();
txn.insert('users', userData);
txn.commit();

// Avoid: Long-running transaction
const txn2 = db.beginTransaction();
for (let i = 0; i < 100000; i++) {
  txn2.insert('logs', { message: `Log ${i}` });
}
txn2.commit(); // This locks the database for too long

Always handle errors

Always use try-catch and rollback on errors:
const txn = db.beginTransaction();

try {
  // Operations
  txn.insert('users', userData);
  txn.commit();
} catch (error) {
  txn.rollback();
  throw error;
}

Don’t reuse transactions

Create a new transaction for each atomic operation:
// Good: New transaction each time
function createUser(data) {
  const txn = db.beginTransaction();
  try {
    const id = txn.insert('users', data);
    txn.commit();
    return id;
  } catch (error) {
    txn.rollback();
    throw error;
  }
}

// Avoid: Reusing transaction
const globalTxn = db.beginTransaction(); // Don't do this

Build docs developers (and LLMs) love