Managing Transactions
1. Using Interactive Transactions
await prisma.$transaction(async (tx) => {
const from = await tx.account.update({ where: { id: 1 }, data: { balance: { decrement: 100 } } });
if (from.balance < 0) throw new Error("Insufficient funds");
await tx.account.update({ where: { id: 2 }, data: { balance: { increment: 100 } } });
}, { timeout: 10_000, maxWait: 5_000, isolationLevel: "Serializable" });
| Option | Detail |
timeout | Max ms for whole transaction |
maxWait | Wait for a connection |
isolationLevel | Per-transaction isolation |
2. Creating Sequential Transactions
const [user, posts] = await prisma.$transaction([
prisma.user.create({ data: { email } }),
prisma.post.createMany({ data: items })
]);
| Aspect | Detail |
| Atomic | All or none |
| No conditional logic | Promises declared up front |
3. Implementing Nested Writes
| Pattern | Detail |
| Single call | Nested create/update wraps in implicit transaction |
| Cross-model | Use $transaction explicitly |
4. Using Transaction Isolation Levels
| Level | PG / MySQL |
ReadUncommitted | Lowest, may see uncommitted |
ReadCommitted | Default for PG |
RepeatableRead | Default for MySQL |
Serializable | Highest; conflicts retried |
5. Handling Transaction Rollbacks
| Trigger | Effect |
| Thrown error | Rolls back |
| Timeout | Rolls back |
| tx.$rollback() | Not exposed; throw to abort |
6. Setting Transaction Timeout
| Option | Detail |
| Default | 5000ms (interactive), unbounded (sequential) |
| Global | Set via transactionOptions on client |
7. Using Idempotent Operations
| Pattern | Use |
| upsert | Safe retry |
| createMany + skipDuplicates | Idempotent bulk insert |
| Unique tokens | Idempotency keys per request |
8. Implementing Optimistic Concurrency
const res = await prisma.product.updateMany({
where: { id, version },
data: { price, version: { increment: 1 } }
});
if (res.count === 0) throw new Error("Concurrent modification");
| Aspect | Detail |
| Version column | Increment on every write |
| Conflict | updateMany returns 0 |
9. Avoiding Long-Running Transactions
| Tip | Detail |
| Short body | Don't call external APIs inside |
| Batch outside | Pre-compute then write |
| Connection blocking | Long tx holds a pool slot |
10. Handling Transaction Errors
| Error | Action |
P2028 | Transaction timeout — retry |
P2034 | Write conflict (serializable) — retry |
| Generic | Log + abort |