./scripts/start-local-db.sh # Start PostgreSQL container
npx prisma migrate dev # Run migrations
npx prisma studio # GUI for database
npx prisma generate # Regenerate client after schema changes// src/lib/prisma.ts
import { PrismaClient } from '@prisma/client'
const globalForPrisma = global as unknown as { prisma: PrismaClient }
export const prisma = globalForPrisma.prisma || new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prismaDev mode uses global singleton to survive hot reload.
- Edit
prisma/schema.prisma - Run
npx prisma migrate dev --name descriptive_name - Commit migration file + schema changes together
// src/lib/api.ts - createExpense
await prisma.expense.create({
data: {
groupId,
title,
amount,
paidById: paidBy,
splitMode,
expenseDate,
paidFor: {
createMany: {
data: paidFor.map(({ participant, shares }) => ({
participantId: participant,
shares,
})),
},
},
},
})// Expenses with payer and split details
await prisma.expense.findMany({
where: { groupId },
include: {
paidBy: true,
paidFor: { include: { participant: true } },
category: true,
},
orderBy: [{ expenseDate: 'desc' }, { createdAt: 'desc' }],
})// Update expense and replace paidFor entries
await prisma.expense.update({
where: { id: expenseId },
data: {
title,
amount,
paidFor: {
deleteMany: {}, // Remove all existing
createMany: { data: newPaidFor },
},
},
})Used for atomic operations:
// src/lib/api.ts - createRecurringExpenses
await prisma.$transaction(async (tx) => {
const expense = await tx.expense.create({ data: expenseData })
await tx.recurringExpenseLink.update({
where: { id: linkId },
data: { nextExpenseCreatedAt: nextDate },
})
return expense
})All monetary values stored as integers in cents:
100= $1.0015050= $150.50
Split shares vary by mode:
EVENLY: 1 per participantBY_SHARES: Weight integers (1, 2, 3...)BY_PERCENTAGE: Basis points (2500 = 25%)BY_AMOUNT: Cents directly