The fastest and simplest library for SQLite3 in Node.js with synchronous API for high-performance database operations
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Transaction system providing ACID compliance with support for nested transactions using savepoints and multiple transaction types.
Create transaction wrapper functions that automatically handle BEGIN/COMMIT/ROLLBACK operations.
/**
* Creates a transaction function wrapper
* @param {Function} fn - Function to execute within transaction
* @returns {TransactionFunction} Transaction function with transaction type variants
*/
transaction(fn);
interface TransactionFunction {
(...args): any; // Default transaction using BEGIN
deferred(...args): any; // Deferred transaction using BEGIN DEFERRED
immediate(...args): any; // Immediate transaction using BEGIN IMMEDIATE
exclusive(...args): any; // Exclusive transaction using BEGIN EXCLUSIVE
readonly database: Database; // Associated database instance
}Usage Examples:
// Create transaction function
const insertMany = db.transaction((users) => {
const insert = db.prepare('INSERT INTO users (name, email) VALUES (@name, @email)');
for (const user of users) {
insert.run(user);
}
});
// Execute transaction
insertMany([
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
{ name: 'Charlie', email: 'charlie@example.com' }
]);
// All users inserted atomically, or none if any failsDifferent transaction types provide various levels of locking and concurrency control.
// Transaction type variants
transactionFunction(); // BEGIN (same as deferred)
transactionFunction.deferred(); // BEGIN DEFERRED (default SQLite behavior)
transactionFunction.immediate(); // BEGIN IMMEDIATE (acquire reserved lock immediately)
transactionFunction.exclusive(); // BEGIN EXCLUSIVE (acquire exclusive lock immediately)Transaction Type Behaviors:
const updateInventory = db.transaction((items) => {
const update = db.prepare('UPDATE inventory SET quantity = quantity - ? WHERE item_id = ?');
for (const item of items) {
update.run(item.quantity, item.id);
}
});
// Deferred transaction - acquire locks as needed (default)
updateInventory.deferred(items);
// Immediate transaction - acquire reserved lock immediately
// (prevents other writers from starting)
updateInventory.immediate(items);
// Exclusive transaction - acquire exclusive lock immediately
// (prevents all other connections from reading or writing)
updateInventory.exclusive(items);Transactions can be nested using SQLite savepoints for complex error handling scenarios.
// Nested transactions automatically use savepoints
// Inner transaction failures roll back to savepoint, not beginningUsage Examples:
const insertUser = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
const insertProfile = db.prepare('INSERT INTO profiles (user_id, bio) VALUES (?, ?)');
const insertPreferences = db.prepare('INSERT INTO preferences (user_id, theme) VALUES (?, ?)');
// Outer transaction
const createUserWithProfile = db.transaction((userData) => {
// Insert user
const userResult = insertUser.run(userData.name, userData.email);
const userId = userResult.lastInsertRowid;
// Inner transaction for profile creation
const createProfile = db.transaction((profileData) => {
insertProfile.run(userId, profileData.bio);
if (profileData.preferences) {
// Nested inner transaction for preferences
const setPreferences = db.transaction((prefs) => {
insertPreferences.run(userId, prefs.theme);
if (prefs.theme === 'invalid') {
throw new Error('Invalid theme'); // This will rollback only preferences
}
});
setPreferences(profileData.preferences);
}
});
try {
createProfile(userData.profile);
} catch (error) {
console.log('Profile creation failed, but user was created');
// User insert is preserved, only profile operations rolled back
}
});
// Execute the nested transaction
createUserWithProfile({
name: 'John Doe',
email: 'john@example.com',
profile: {
bio: 'Software developer',
preferences: { theme: 'dark' }
}
});Transactions automatically handle errors with proper rollback behavior.
// Automatic rollback on any thrown error
// Error propagates normally after rollback
// Nested transactions use savepoints for partial rollbackUsage Examples:
const transferFunds = db.transaction((fromAccount, toAccount, amount) => {
const debit = db.prepare('UPDATE accounts SET balance = balance - ? WHERE id = ?');
const credit = db.prepare('UPDATE accounts SET balance = balance + ? WHERE id = ?');
const getBalance = db.prepare('SELECT balance FROM accounts WHERE id = ?');
// Check sufficient funds
const fromBalance = getBalance.get(fromAccount).balance;
if (fromBalance < amount) {
throw new Error('Insufficient funds'); // Transaction will rollback
}
// Perform transfer
debit.run(amount, fromAccount);
credit.run(amount, toAccount);
// Verify the transfer
const newFromBalance = getBalance.get(fromAccount).balance;
const newToBalance = getBalance.get(toAccount).balance;
return {
fromBalance: newFromBalance,
toBalance: newToBalance,
transferred: amount
};
});
try {
const result = transferFunds(1, 2, 100);
console.log('Transfer completed:', result);
} catch (error) {
console.log('Transfer failed:', error.message);
// Database state unchanged due to automatic rollback
}Transaction functions expose properties for introspection and database access.
interface TransactionFunction {
readonly database: Database; // Associated database instance
// All transaction type variants (deferred, immediate, exclusive) have same properties
}Usage Examples:
const batchInsert = db.transaction((data) => {
// Function implementation
});
// Access the associated database
console.log(batchInsert.database === db); // true
// All transaction variants share the same database reference
console.log(batchInsert.deferred.database === db); // true
console.log(batchInsert.immediate.database === db); // true
console.log(batchInsert.exclusive.database === db); // true
// Check if currently in transaction during execution
const checkTransactionStatus = db.transaction(() => {
console.log(db.inTransaction); // true during transaction execution
});
checkTransactionStatus();
console.log(db.inTransaction); // false after transaction completesGuidelines for effective transaction usage and performance optimization.
Performance Considerations:
// Good: Batch multiple operations in a single transaction
const batchUserInsert = db.transaction((users) => {
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
for (const user of users) {
insert.run(user.name, user.email);
}
});
// Bad: Individual transactions for each operation (slow)
function insertUsersSlow(users) {
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
for (const user of users) {
const singleInsert = db.transaction(() => {
insert.run(user.name, user.email);
});
singleInsert(); // Creates transaction overhead for each insert
}
}
// Good: Short-lived transactions
const quickUpdate = db.transaction((id, newValue) => {
db.prepare('UPDATE table SET value = ? WHERE id = ?').run(newValue, id);
});
// Avoid: Long-running transactions that hold locks
const avoidLongTransaction = db.transaction(() => {
// Don't do this - holds database locks too long
for (let i = 0; i < 1000000; i++) {
// Long-running computation
heavyComputation();
}
db.prepare('INSERT INTO results VALUES (?)').run(result);
});Error Handling Patterns:
// Pattern: Specific error handling with graceful degradation
const safeUserCreation = db.transaction((userData) => {
const insertUser = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
const insertAuditLog = db.prepare('INSERT INTO audit_log (action, details) VALUES (?, ?)');
try {
const result = insertUser.run(userData.name, userData.email);
// Nested transaction for audit log (can fail without affecting user creation)
const logAudit = db.transaction(() => {
insertAuditLog.run('user_created', JSON.stringify(userData));
});
try {
logAudit();
} catch (auditError) {
console.warn('Audit logging failed:', auditError.message);
// User creation still succeeds
}
return result.lastInsertRowid;
} catch (error) {
console.error('User creation failed:', error.message);
throw error; // Re-throw to trigger rollback
}
});While transaction functions are recommended, manual transaction control is also possible.
// Manual transaction control (not recommended with transaction functions)
db.exec('BEGIN');
try {
db.prepare('INSERT INTO users (name) VALUES (?)').run('John');
db.prepare('INSERT INTO profiles (user_id) VALUES (?)').run(1);
db.exec('COMMIT');
} catch (error) {
db.exec('ROLLBACK');
throw error;
}
// Warning: Don't mix manual control with transaction functions
const mixedApproach = db.transaction(() => {
// Don't use raw COMMIT/ROLLBACK inside transaction functions
// db.exec('COMMIT'); // This will cause issues
});