Skip to content

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.prisma is 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:generate and the client's types still describe the old shape. The error points at your call site, not the schema.
Show 3 more
  • Serverless needs a pooler. On Vercel, point DATABASE_URL at your database's pooled endpoint for the app. The CLI finds the direct one for migrations through prisma.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.

Database
NeonSupabase
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.ts exports the only PrismaClient in 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 in next dev a per-module instance is recreated on every hot reload until Postgres refuses new connections.
  • Model types, enums and the Prisma namespace 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, pg pool 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 use disconnect() from @/db/orm.
  • @/db re-exports the same client as db, plus recordAudit(). 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 prisma directly and live next to the feature that owns the table.
Select only the fields you need
  • Default to select. A bare findMany() 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 (not include) 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: take plus a cursor or skip. An unbounded findMany is 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 use where: { id: { in: ids } }. A findMany followed by await inside a for loop over the results is the bug this rule exists for.
  • Promise.all over a hundred findUnique calls is still a hundred queries. It is faster than the loop and still wrong.
  • For counts and sums use _count, aggregate or groupBy rather than loading rows and reducing in JavaScript.
Raw SQL
  • Use the typed API first. When you genuinely need SQL, use the tagged template sql from @/db/orm or prisma.$queryRaw: values inside ${} become bind parameters.
  • $queryRawUnsafe and $executeRawUnsafe are 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 upsert over 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 DateTime as 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, and src/db/driver.ts pins local sessions where it can. bun run verify fails 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 run bun 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 add prisma/migrations to .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 with bunx 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" with output = "../src/generated/prisma". That folder is git-ignored build output that postinstall rewrites. Never edit it, never commit it.
  • No url in the datasource block. The app connects through src/db/driver.ts; the CLI reads the direct connection string from prisma.config.ts, which also loads .env.local.
  • migrate dev and db push no longer regenerate the client, and nothing seeds automatically. The db:migrate and db:push scripts generate first; seed with bun 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:push is allowed only against a local or throwaway branch database, while you are still shaping a model.
  • Never run db push against staging or production, and never against a database whose DATABASE_URL you 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, never migrate dev, which can reset.
Writing models
  • Model names are singular PascalCase (User, SubscriptionItem); use @@map to point them at snake_case table names, and @map for 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 Int count of minor units (amountCents Int), never Float and never Decimal. A currency's exponent is not always 2 (JPY and KRW have none, KWD has three) so store minor units and format with Intl.NumberFormat, which knows the exponent.
  • Timestamps are DateTime @db.Timestamptz with no precision argument. @db.Timestamptz(3) truncates to milliseconds, and the Drizzle battery's timestamp(..., { withTimezone: true }) is bare TIMESTAMPTZ; 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.Uuid when you want the database to mint them: @default(uuid()) generates in the client instead, which is not the same column.
  • @updatedAt on every table that is mutated, so drift is debuggable.
  • Text that could be one of a fixed set is an enum, not a String with 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

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.

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.