ORM
Next.js boilerplate with Prisma
Schema-first Postgres ORM with generated types, real migrations and a GUI.
Prisma 7 ORM on the driver adapter for your database, with a client that survives hot reload. The schema is slotted, so every other battery writes its tables into it. Committed SQL migrations and a seed script are included.
What Prisma adds to the agent layer: 2 rules · 2 skills · 5 solution docs
Maintained by @raviMITNext.js on Vercel
From the manifest
Should you pick Prisma?
Pick it if
Teams who want one declarative schema file as the source of truth. You get a migration history you can read in review, and a client whose types you never hand-write. Strong pick when several people touch the data model, or when non-backend contributors need Studio to look at rows.
Watch out for
- One more schema language to learn.
schema.prismais not TypeScript, so codegen and a generate step sit between you and your types. That is the price of the ergonomics. - A generate step you can forget. Edit the schema without running
bun run db:generateand the client's types still describe the old shape. The error points at your call site, not the schema.
Show 3 moreShow fewer
- Serverless needs a pooler. On Vercel, point
DATABASE_URLat your database's pooled endpoint for the app. The CLI finds the direct one for migrations throughprisma.config.ts. - No Rust engine at runtime. Prisma 7 plans queries in TypeScript and sends them through a driver adapter for your database, so the app bundle is lighter than Prisma 6's. The CLI still downloads a schema engine binary for migrations.
- Most SQL you need has a first-class API. The rest goes through
$queryRaw, which is less fluent than a SQL-shaped builder.
What it costs
Prisma ORM, CLI, migrations and Studio are free and open source (Apache 2.0). Prisma's hosted products (Prisma Postgres, the Accelerate cache) have a free plan, then usage-based paid plans from $10/month.
Prices change. Check with Prisma before you commit.
registry/tested.yaml
Tested with Prisma
Each pair was installed, typechecked, linted, built and booted together.
- Admin panel
- Admin panel
- Error tracking
- Sentry
- Customer support
- Crisp
What it adds
What Prisma adds to the repo
Read straight from the prisma manifest, so it is exactly what lands in your repo.
Environment variables
SEED_ALLOW_REMOTEOptional
Guard on
bun run db:seed. The seed refuses to run against a connection string that does not look local unless this is "1". Leave it unset: the one time you need it you will know, and every other time it is the difference between seeding your laptop and overwriting staging.- Where to get it
- Set it inline for a single run: SEED_ALLOW_REMOTE=1 bun run db:seed
- Placeholder
- 0
PRISMA_LOG_QUERIESOptional
Set to "1" for one debugging session to log every query Prisma runs. Off by default because the output is enormous and interpolated values (emails, tokens, ids) end up in your terminal scrollback and in whatever collects your logs.
- Placeholder
- 0
Dependencies
- @prisma/client^7.10.0
- prisma^7.10.0dev
Scripts
- bun run db:generate
bunx prisma generate
- bun run db:migrate
bunx prisma generate && bunx prisma migrate dev
- bun run db:migrate:deploy
bunx prisma migrate deploy
- bun run db:push
bunx prisma generate && bunx prisma db push
- bun run db:seed
bun prisma/seed.ts
- bun run db:studio
bunx prisma studio
- bun run postinstall
bunx prisma generate
Files it writes
13 files, at these exact paths.
prisma/2 files
- schema.prisma
- seed.ts
src/6 files
db/6 files
- audit.ts
- connection-url.ts
- load-env.ts
- orm.ts
- prisma.ts
- verify.ts
variants/4 files
db-neon/2 files
src/2 files
db/2 files
- direct-url.ts
- driver.ts
db-supabase/2 files
src/2 files
db/2 files
- direct-url.ts
- driver.ts
- prisma.config.ts
Stack slots it fills
The stack declares these injection points; this battery supplies the fragment, so the provider tree, the env check and the schema stay one file each instead of many.
- @slot db-orm
- @slot db-schema
- @slot verify-checks
The differentiator
What Prisma teaches your agent
Other starter kits stop at the package. This is the part an agent reads: where it may work, what it must never do there, and the problems someone already solved.
Rules (2)
Loaded when the agent opens a matching file.
Querying with Prisma
Loads onsrc/db/**src/lib/**src/app/**.claude/rules/prisma-client.md
One client, imported from one place
src/db/prisma.tsexports the onlyPrismaClientin this repo. Import it:import { prisma } from "@/db/prisma";- Never write
new PrismaClient()anywhere else, not in a route handler, not in a helper, not in a test setup file. Each instance is its own connection pool, and innext deva per-module instance is recreated on every hot reload until Postgres refuses new connections. - Model types, enums and the
Prismanamespace come from the generated client:import type { User, Prisma } from "@/generated/prisma/client";. Never import from@prisma/client. Since Prisma 7 it is only the runtime the generated client uses, and it exports no models. - The client runs on the driver adapter in
src/db/driver.ts. Do not build a second adapter,pgpool or Neon pool for Prisma somewhere else; change the pool settings there. - Do not call
prisma.$disconnect()in request-handling code. It belongs in one-shot scripts only, which usedisconnect()from@/db/orm. @/dbre-exports the same client asdb, plusrecordAudit(). Code that has to compile under either ORM imports from there and nowhere else: there is no ORM-neutral query layer in this repo, and@/db/orm(sql,ping,transaction,disconnect) is Prisma-specific by design.- Feature queries import
prismadirectly and live next to the feature that owns the table.
Select only the fields you need
- Default to
select. A barefindMany()fetches every column of every row, including the ones you added last week (password hashes, tokens, large JSON blobs) and then serialises them across the server/client boundary. - Use
select(notinclude) when you need a subset of a relation:select: { id: true, author: { select: { id: true, name: true } } }. - Never return a raw Prisma model straight to a client component. Map it to an explicit response shape so a new column cannot leak by accident.
- Always bound a list query:
takeplus a cursor orskip. An unboundedfindManyis a production incident waiting for your table to grow.
Do not hand-roll N+1
- Fetch relations in one query with
include/select, or batch the ids and usewhere: { id: { in: ids } }. AfindManyfollowed byawaitinside aforloop over the results is the bug this rule exists for. Promise.allover a hundredfindUniquecalls is still a hundred queries. It is faster than the loop and still wrong.- For counts and sums use
_count,aggregateorgroupByrather than loading rows and reducing in JavaScript.
Raw SQL
- Use the typed API first. When you genuinely need SQL, use the tagged template
sqlfrom@/db/ormorprisma.$queryRaw: values inside${}become bind parameters. $queryRawUnsafeand$executeRawUnsafeare banned in this repo. There is no user input safe enough to concatenate into SQL.- Raw results are not validated by Prisma. Type the return and check it before trusting the shape.
Transactions and writes
- Multi-table writes that must succeed together go through
transaction()from@/db/orm. - Keep transactions short and free of network calls. Charge the card, then open the transaction to record it, not the other way around.
- Prefer
upsertover read-then-write for anything that can race, and rely on a unique constraint rather than a "does it exist?" query.
Timestamps are UTC
- Prisma 7's driver adapters send a
DateTimeas UTC text with no offset, and Postgres reads it in the session's time zone. So the database session must run in UTC. Hosted Neon and Supabase do, andsrc/db/driver.tspins local sessions where it can.bun run verifyfails on any other zone. - A timestamp that looks shifted by your UTC offset is this problem. Fix the
database's zone (
alter database ... set timezone to 'UTC'), never the value in application code.
After a schema edit
Running the app before bun run db:generate gives you type errors that point
at your call site and describe a model shape that no longer exists. Generate
first, then debug.
Prisma schema and migrations
Loads onprisma/**.claude/rules/prisma-schema.md
prisma/schema.prisma is the source of truth for the database, and
prisma/migrations/ is the history of how it got there. Both are code, both
are reviewed, both are committed.
Changing the schema
- Edit
schema.prisma, then immediately runbun run db:migrate. That regenerates the client, writes a migration and applies it locally. A schema edit without a migration is an unfinished change. - Commit
prisma/migrations/**in the same commit as the schema edit. Never addprisma/migrationsto.gitignore, never delete or rewrite a migration that has already been pushed: production replays that exact directory. - Never hand-edit a migration Prisma has already applied. To fix a mistake, write a new migration.
- When Prisma prints a data-loss warning during
db:migrate, stop and read it. If the answer is a backfill, write the migration withbunx prisma migrate dev --create-only, add the SQL by hand, then apply.
Prisma 7 setup: leave it as it is
- The generator block stays
provider = "prisma-client"withoutput = "../src/generated/prisma". That folder is git-ignored build output thatpostinstallrewrites. Never edit it, never commit it. - No
urlin thedatasourceblock. The app connects throughsrc/db/driver.ts; the CLI reads the direct connection string fromprisma.config.ts, which also loads.env.local. migrate devanddb pushno longer regenerate the client, and nothing seeds automatically. Thedb:migrateanddb:pushscripts generate first; seed withbun run db:seed.- The CLI may print "Update available" for a new major version. Ignore it. Moving to a new Prisma major is its own reviewed change, never a side effect.
db push is a local scratchpad
bun run db:pushis allowed only against a local or throwaway branch database, while you are still shaping a model.- Never run
db pushagainst staging or production, and never against a database whoseDATABASE_URLyou did not set yourself in this shell. It writes no history and resolves a mismatch by dropping columns and tables. - Production only ever gets
prisma migrate deploy, and only from the build or a deliberate one-off command, nevermigrate dev, which can reset.
Writing models
- Model names are singular
PascalCase(User,SubscriptionItem); use@@mapto point them atsnake_casetable names, and@mapfor columns where the database name differs. - Every foreign key needs an explicit relation and an index. Prisma indexes the
implicit side of a one-to-many for you; a filtered or sorted column does not
get one automatically: add
@@index. - Money is an
Intcount of minor units (amountCents Int), neverFloatand neverDecimal. A currency's exponent is not always 2 (JPY and KRW have none, KWD has three) so store minor units and format withIntl.NumberFormat, which knows the exponent. - Timestamps are
DateTime @db.Timestamptzwith no precision argument.@db.Timestamptz(3)truncates to milliseconds, and the Drizzle battery'stimestamp(..., { withTimezone: true })is bareTIMESTAMPTZ; a repo that switches ORM must not silently change its column types. - Ids are
String @id @default(cuid())unless something external owns the id, or@default(dbgenerated("gen_random_uuid()")) @db.Uuidwhen you want the database to mint them:@default(uuid())generates in the client instead, which is not the same column. @updatedAton every table that is mutated, so drift is debuggable.- Text that could be one of a fixed set is an
enum, not aStringwith comments.
The slot marker
The generated block at the bottom of the schema is where the batteries you selected inserted their tables when this repo was generated. It is a normal comment now. Add your own models above or below it; do not delete tables that a battery owns (auth sessions, billing subscriptions) just because your feature does not use them.
The seed
prisma/seed.ts must stay idempotent and deterministic: upsert, no
Date.now(), no random ids, no real customer data. Anyone should be able to
run bun run db:seed twice and get the same database.
Skills (2)
Invoked by name.
- /add-model
Add a Prisma model (or a field on an existing one), migrate it, regenerate the client and wire up the first typed query.
.claude/skills/add-model/SKILL.md
- /migrate
Create, inspect, apply and recover Prisma migrations safely: locally, on a branch database and in production.
.claude/skills/migrate/SKILL.md
Solution docs (5)
Written before you hit the problem. Each one ships in the repo at docs/solutions/ and is published here as a cookbook page.
- Prisma types are wrong after a schema edit: generated client driftThe Prisma client is generated code. Edit the schema without regenerating and TypeScript describes the old database, locally, in CI-free builds and after a cached deploy.docs/solutions/prisma/client-drift-after-schema-edit.md
- Prisma in dev: "too many clients already" after a few savesNext.js hot reload re-runs your module and builds a new PrismaClient every time. Cache one instance on globalThis and the connection leak stops.docs/solutions/prisma/hot-reload-client-leak.md
- Running Prisma migrations on Vercel without breaking productionPut prisma generate and prisma migrate deploy in the Vercel build command, never db push or migrate dev, and design migrations to survive a rolling deploy.docs/solutions/prisma/migrate-deploy-on-vercel.md
- Fixing N+1 queries in Prisma with include, select and groupByA loop that queries per row turns one page load into hundreds of round trips. Fetch relations in the parent query, batch by id, and aggregate in the database.docs/solutions/prisma/n-plus-one-with-include.md
- Prisma on serverless: why you need a pooler URL and a direct URLEach serverless instance opens its own Postgres pool, so traffic exhausts connections. Route runtime queries through a pooler and keep a direct URL for migrations.docs/solutions/prisma/serverless-connection-pooling.md
How it fits
What Prisma needs, and what it goes well with
The resolver enforces this before it generates anything, and names every addition it makes.
Requires
- A database battery. The resolver adds the default one for you and tells you why.
Pairs well with
Nothing extra. Add any tested battery alongside Prisma.
Cannot be combined with
Compared with the alternatives
Build a repo with Prisma
Free and MIT. The builder opens with Prisma picked. You download the zip right away, and we email you the link too.