Overview

schema.prisma defines the models, relations, and generator that Prisma turns into migrations and the typed client. In Prisma 7 it no longer holds the connection URL. Treat the schema as the contract and everything else as derived from it.

Declare the provider only; put the URL in prisma.config.ts

Prisma 7 moved url, directUrl, and shadowDatabaseUrl out of the schema and into prisma.config.ts. The datasource block names the provider and, optionally, relationMode.

datasource db {
  provider = "postgresql"
}

Do not switch providers without regenerating migrations; the provider selects the SQL dialect and the available native types. Keep the URL in an environment variable loaded by the config file; see prisma.

Use the prisma-client generator and set output

generator client {
  provider = "prisma-client"
  output   = "../src/generated/prisma"
}
  • prisma-client replaces the deprecated prisma-client-js. output is required and must sit in your source tree, not node_modules.
  • Add the output directory to .gitignore and regenerate in postinstall; see prisma-client.
  • Do not list driverAdapters in previewFeatures; adapters are standard in Prisma 7. Add previewFeatures only for a capability that is still flagged.
  • A schema can be split across files. Point schema in prisma.config.ts at a folder.

Give every model an id and choose its generator deliberately

Prisma requires one @id or @@id per model.

model Product {
  id        String   @id @default(uuid(7))
  sku       String   @unique
  price     Int
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
  • cuid(), uuid(), and uuid(7) are generated by Prisma Client, not by the database. Prefer uuid(7) (time-ordered) over uuid() for index locality. To let Postgres 18 generate it, use @default(dbgenerated("uuidv7()")) @db.Uuid; see postgres.
  • Use autoincrement() when one database assigns ids.
  • @updatedAt is maintained by Prisma on each update. @default(now()) sets createdAt; do not compute it in application code.

Put uniqueness and indexes in the schema

  • @unique for single-field natural keys (email, slug, external id); @@unique([a, b]) for composite keys.
  • @@index([col]) for columns you filter or sort on. Match each index to a real query; see postgres-indexes.
  • Indexes and constraints Prisma cannot express belong in a hand-edited migration; see prisma-migrations.

Declare relations on both sides with explicit onDelete

model Order {
  id     String      @id @default(uuid(7))
  userId String
  user   User        @relation(fields: [userId], references: [id], onDelete: Cascade)
  items  OrderItem[]
}
 
model User {
  id     String  @id @default(uuid(7))
  orders Order[]
}

Specify fields, references, and onDelete. Defaults differ between required and optional relations, so choose Cascade, Restrict, or SetNull per relation instead of inheriting them.

Set relationMode only when the database does not enforce foreign keys

The default, foreignKeys, relies on the database. Use relationMode = "prisma" only for a database without foreign key support; Prisma then emulates the checks in its query layer and does not create indexes on relation columns, so add @@index on every foreign key column yourself. Do not combine it with Postgres, which already enforces foreign keys.

Map to native types when defaults are too loose

  • String maps to text on Postgres. Use @db.VarChar(n) when length matters.
  • Int maps to integer. Use BigInt for values that may pass 2 billion.
  • Json maps to jsonb. Prisma filters Json fields with path, equals, string_contains, and array_contains; Postgres paths are arrays such as ["petName"]. Use prisma-raw-queries for jsonb_path_query and operators Prisma lacks, and see postgres-jsonb.
  • Run prisma migrate dev after changing native types so the ALTER TABLE is generated.