Transactions Guide
This guide follows patterns used in apps/fedaco-e2e/src/test/transaction/*.
Basic Transaction
Use the transaction-scoped connection passed into the callback:
ts
await db().transaction(async (tx) => {
await tx.table('users').insert({ name: 'Alice' });
await tx.table('users').where({ name: 'Alice' }).update({ name: 'Alice A.' });
});Rollback On Error
Throwing inside callback rolls back all changes:
ts
await db().transaction(async (tx) => {
await tx.table('users').insert({ name: 'Alice' });
throw new Error('force rollback');
});Nested Transactions
Nested transactions are supported and tracked by level:
ts
await db().transaction(async () => {
await db().table('users').insert({ name: 'Outer' });
await db().transaction(async () => {
await db().table('users').insert({ name: 'Inner' });
});
});Manual Transaction API
ts
await db().beginTransaction();
try {
await db().table('users').insert({ name: 'Manual' });
await db().commit();
} catch (e) {
await db().rollBack();
throw e;
}Model Queries In Transaction
From e2e, both patterns are valid:
ts
await db().transaction(async (tx) => {
await User.createQuery(tx).create({ name: 'Bob', email: 'bob@example.com' });
await User.createQuery()
.withConnection(tx)
.where('name', 'Bob')
.update({ email: 'bob2@example.com' });
});Transaction Options
ts
await db().transaction(
async (tx) => {
await tx.table('users').insert({ name: 'OptionUser' });
},
{
timeout: 5000,
isolationLevel: 'SERIALIZABLE',
attempts: 3,
isolated: true,
},
);timeout: rolls back if callback exceeds limitisolationLevel: sets SQL isolationattempts: retries on concurrency errors (for example deadlock)isolated: uses dedicated connection; can use connector fallback when no pool manager exists
Transaction Manager Hooks
When needed, set DatabaseTransactionsManager to observe begin/commit/rollback and register callbacks:
ts
const manager = new DatabaseTransactionsManager();
db().setTransactionManager(manager);
await db().transaction(async () => {
await db().table('users').insert({ name: 'Tracked' });
});