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.