Transactions & Data Integrity
Transactions & Data Integrity
Some operations touch several rows or tables and must all succeed or all fail — never half. Transferring money debits one account and credits another; if the second write fails, the first must be undone. A transaction guarantees this all-or-nothing behaviour.
The classic example
Consider a bank transfer. Without a transaction, a crash between the two updates leaves money debited from one account but never credited to the other — corrupt data:
ACID, briefly
Transactions provide ACID guarantees: Atomicity (all-or-nothing), Consistency (valid state to valid state), Isolation (concurrent transactions do not corrupt each other), and Durability (committed data survives crashes).
Transactions with the DataSource
The simplest approach in TypeORM is dataSource.transaction(). Everything inside the callback commits together, or rolls back entirely if anything throws:
manager, not your repositories. Inside the callback, all reads and writes must go through the provided manager (or a query runner) so they share the same transaction. Calling a normal injected repository would run outside the transaction.
The QueryRunner approach
For finer control — explicit commit/rollback, or when you need to release the connection yourself — use a query runner:
release() (use a finally block) leaks connections until the pool is exhausted and the app stops responding.
Keep transactions short
Summary
Transactions make multi-step database changes atomic — all commit or all roll back — preserving data integrity (the ACID guarantees). Use dataSource.transaction() for the common case (operate through its manager), or a QueryRunner for explicit control (always release() in finally). Keep transactions short and free of external calls. Next: a different ORM, Prisma.