Node.js, Prisma & PostgreSQL

Lesson 9 of 12

Transactions and Data Integrity

Some operations only make sense together. If you move XP from one account to another, both the debit and the credit must succeed, or neither should.

Batch transactions

const [fromAccount, toAccount] = await prisma.$transaction([
  prisma.account.update({ where: { id: 1 }, data: { xp: { decrement: 50 } } }),
  prisma.account.update({ where: { id: 2 }, data: { xp: { increment: 50 } } }),
]);

If either update fails, PostgreSQL rolls back both, your data never ends up half-changed.

Interactive transactions

For logic that needs to check something in between steps, pass a function instead:

await prisma.$transaction(async (tx) => {
  const from = await tx.account.findUniqueOrThrow({ where: { id: 1 } });
  if (from.xp < 50) throw new Error('Insufficient XP');

  await tx.account.update({ where: { id: 1 }, data: { xp: { decrement: 50 } } });
  await tx.account.update({ where: { id: 2 }, data: { xp: { increment: 50 } } });
});

Throwing anywhere inside the callback rolls back every query that already ran in that transaction.

📝 Transactions Quiz

Passing score: 70%
  1. 1.What happens if one query inside a prisma.$transaction([...]) call fails?

  2. 2.Throwing an error inside an interactive $transaction callback function ____ every query that already ran in it.

  3. 3.Transactions guarantee that a group of related writes either all succeed or all fail together.