Skip to main content
Transactions allow you to execute a series of queries as a single atomic unit. If any query fails, all changes within the transaction are rolled back. Sequelize supports both managed transactions (automatic commit/rollback) and unmanaged transactions (manual control).

sequelize.transaction()

The primary way to use transactions is through the managed sequelize.transaction() helper:
// Managed transaction — automatically committed on success, rolled back on error
const result = await sequelize.transaction(async (t) => {
  const user = await User.create({ name: 'Alice' }, { transaction: t });
  await Profile.create({ userId: user.id }, { transaction: t });
  return user;
});

Signature

sequelize.transaction<T>(
  callback: (t: Transaction) => PromiseLike<T> | T
): Promise<T>

sequelize.transaction<T>(
  options: ManagedTransactionOptions,
  callback: (t: Transaction) => PromiseLike<T> | T
): Promise<T>
options
ManagedTransactionOptions
When CLS (AsyncLocalStorage) transactions are enabled (the default), any query executed inside the callback automatically inherits the transaction. You don’t need to pass { transaction: t } explicitly.

CLS Transactions

By default, Sequelize uses Node.js AsyncLocalStorage to propagate transactions through async contexts. This means any query executed inside sequelize.transaction() is automatically enrolled in the transaction:
await sequelize.transaction(async () => {
  // No need to pass { transaction: t } — CLS handles it automatically
  await User.create({ name: 'Bob' });
  await Post.create({ title: 'Hello' });
});
To disable this behavior, set disableClsTransactions: true in the Sequelize constructor.

Unmanaged Transactions

For manual control over commit and rollback:
const t = await sequelize.startUnmanagedTransaction();

try {
  await User.create({ name: 'Charlie' }, { transaction: t });
  await t.commit();
} catch (error) {
  await t.rollback();
  throw error;
}
With unmanaged transactions you are responsible for always calling either commit() or rollback(). Forgetting to do so will leave the connection in an unusable state and exhaust your connection pool.

Transaction Class

Properties

id
string
A unique identifier for the transaction. For nested transactions (savepoints), this is the same as the parent transaction’s id.
options
NormalizedTransactionOptions
The normalized options this transaction was created with (isolation level, readOnly, etc.).
parent
Transaction | null
The parent transaction if this is a savepoint, otherwise null.
finished
'commit' | 'rollback' | undefined
The current completion state of the transaction. undefined while still in progress.
rootTransaction
Transaction
The topmost transaction in a nested chain. Returns this if not nested.

commit()

transaction.commit(): Promise<void>
Commits the transaction. Throws if the transaction has already been committed or rolled back. For savepoint transactions (nested), calling commit() is a no-op — the savepoint is released by the parent transaction committing.

rollback()

transaction.rollback(): Promise<void>
Rolls back (aborts) the transaction. For savepoint transactions, rolls back to the savepoint.

afterCommit()

transaction.afterCommit(callback: (t: Transaction) => void | Promise<void>): this
Registers a callback to run after the transaction is successfully committed. Multiple callbacks can be registered; they execute in registration order.
await sequelize.transaction(async (t) => {
  const user = await User.create({ name: 'Dave' }, { transaction: t });

  t.afterCommit(() => {
    sendWelcomeEmail(user.email);
  });
});

afterRollback()

transaction.afterRollback(callback: (t: Transaction) => void | Promise<void>): this
Registers a callback to run after the transaction is rolled back.

afterTransaction()

transaction.afterTransaction(callback: (t: Transaction) => void | Promise<void>): this
Registers a callback to run after the transaction completes, regardless of whether it was committed or rolled back.

IsolationLevel Enum

Controls the degree to which the transaction is isolated from the effects of other concurrent transactions.
import { IsolationLevel } from '@sequelize/core';
ValueSQLDescription
IsolationLevel.READ_UNCOMMITTEDREAD UNCOMMITTEDAllows dirty reads. The lowest isolation level.
IsolationLevel.READ_COMMITTEDREAD COMMITTEDPrevents dirty reads. Default in most databases.
IsolationLevel.REPEATABLE_READREPEATABLE READPrevents dirty and non-repeatable reads.
IsolationLevel.SERIALIZABLESERIALIZABLEFull isolation. Prevents dirty reads, non-repeatable reads, and phantom reads.
await sequelize.transaction(
  { isolationLevel: IsolationLevel.SERIALIZABLE },
  async (t) => {
    // ...
  }
);

TransactionNestMode Enum

Controls what happens when sequelize.transaction() is called inside another active transaction.
import { TransactionNestMode } from '@sequelize/core';
ValueBehavior
TransactionNestMode.reuse(Default) Reuses the parent transaction if options are compatible. Throws if incompatible.
TransactionNestMode.savepointCreates a savepoint on the parent transaction. Rolls back to the savepoint on error.
TransactionNestMode.separateAlways starts a new transaction on a separate connection. Use with caution — can cause deadlocks.
// Change the default nest mode for all transactions
const sequelize = new Sequelize({
  dialect: PostgresDialect,
  defaultTransactionNestMode: TransactionNestMode.savepoint,
  // ...
});

// Override for a single call
await sequelize.transaction(
  { nestMode: TransactionNestMode.savepoint },
  async (t) => { /* ... */ }
);

TransactionType Enum

SQLite only. Controls when the database file lock is acquired.
import { TransactionType } from '@sequelize/core';
ValueBehavior
TransactionType.DEFERRED(Default) Lock is not acquired until the first read or write.
TransactionType.IMMEDIATEAcquires a reserved lock immediately, allowing reads but blocking other writes.
TransactionType.EXCLUSIVEAcquires an exclusive lock immediately, blocking all other connections.

Lock Enum

Row-level locking options used with findAll, findOne, etc.
import { Lock } from '@sequelize/core';

const users = await User.findAll({
  where: { id: [1, 2, 3] },
  transaction: t,
  lock: Lock.UPDATE,
});
ValueSQLDescription
Lock.UPDATEFOR UPDATELocks rows for update.
Lock.SHAREFOR SHARELocks rows for share (allows concurrent reads).
Lock.KEY_SHAREFOR KEY SHAREPostgres 9.3+. Locks key columns only.
Lock.NO_KEY_UPDATEFOR NO KEY UPDATEPostgres 9.3+. Locks non-key columns.

Examples

// CLS propagates the transaction automatically
await sequelize.transaction(async () => {
  const order = await Order.create({ userId: 1, total: 99.99 });
  await OrderItem.bulkCreate([
    { orderId: order.id, productId: 5, qty: 2 },
    { orderId: order.id, productId: 8, qty: 1 },
  ]);
  // Automatically committed if no error is thrown
});

Build docs developers (and LLMs) love