Appearance
Database
PostgreSQL via Prisma ORM. Schema lives at prisma/schema.prisma; the generated client is output to src/generated/prisma (gitignored, regenerated by bun run prisma:generate).
Models
Better Auth-managed tables (comment in the schema says "do not modify" - Better Auth owns their shape):
Session-id,expiresAt,token(unique),ipAddress?,userAgent?, timestamps, belongs toUser(cascade delete).Account-id,accountId,providerId, OAuth/credential fields (accessToken,refreshToken,idToken, expiry timestamps,scope,password), belongs toUser(cascade delete).Verification-id,identifier,value,expiresAt, timestamps.
Application user (also managed by Better Auth, via its Prisma adapter):
User-id,name,email(unique),emailVerified,image?, timestamps. Relations:sessions[],accounts[],posts[].
Example domain model (comment says "replace with your own" - this is the one meant to be deleted/extended):
Post-id(cuid),title,content,published(defaultfalse), timestamps, belongs toUser(cascade delete, indexed onuserId). Backs thedemo/postsfeature - seefeatures.md.
Migration workflow
bash
bun run prisma:migrate # create + apply a migration in dev (interactive)
bun run prisma:generate # regenerate the TypeScript client after schema changes
bun run prisma:migrate-deploy # apply pending migrations non-interactively (used in prod)
bun run prisma:studio # open Prisma Studio, a GUI for browsing/editing dataAlways run prisma:generate after prisma:migrate (or after pulling schema changes from git) - the generated client is what src/lib/db.ts imports, and it goes stale as soon as the schema changes.
In production, bun run prisma:migrate-deploy runs automatically on every container start - see docker-entrypoint.sh and deployment.md.
The Prisma client singleton
src/lib/db.ts exports a single cached PrismaClient instance, cached on global in development. Next.js's dev server hot-reloads modules on every file save; without this cache, every reload would create a fresh PrismaClient (and a fresh connection pool) without closing the old one, quickly exhausting Postgres's connection limit. Production doesn't hot-reload, so no caching workaround is needed there - every feature router imports the same singleton via import prisma from "@/lib/db".