Never half a transfer
Moving money is two writes: take from one account, add to another. If the second fails, the first must be undone. A transaction groups writes so they all succeed or all roll back.
Two styles of $transaction
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
const [accounts, total] = await prisma.$transaction([
prisma.account.findMany({ orderBy: { id: "asc" } }),
prisma.account.aggregate({ _sum: { balance: true } }),
]);
console.log(accounts.length, total._sum.balance);
try {
await prisma.$transaction(async (tx) => {
await tx.account.update({ where: { owner: "Bikash" }, data: { balance: { decrement: 800 } } });
const b = await tx.account.findUnique({ where: { owner: "Bikash" } });
if (b.balance < 0) throw new Error("Insufficient funds");
await tx.account.update({ where: { owner: "Asha" }, data: { balance: { increment: 800 } } });
});
} catch (e) {
console.log("rolled back:", e.message);
}
console.log(await prisma.account.findMany({ select: { owner: true, balance: true }, orderBy: { id: "asc" } }));Keep interactive transactions short — they hold locks. Real Prisma also accepts { isolationLevel, timeout } options.