Overview
schema.prisma is the single source of truth for your database shape. It defines the datasource (which database to connect to), the generator (what client to emit), and every model (table), field (column), and relation (foreign key). Prisma reads this file to generate migrations and the typed client. Treat it the way you treat compiled code: the schema is the contract; everything else is derived.
Declare one datasource and pin the provider
A schema.prisma file supports exactly one datasource block.
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}Pin the provider to postgresql, mysql, or sqlite. Do not switch providers without regenerating all migrations from scratch. Provider determines which SQL dialect Prisma emits and which native types are available. Use env() for the URL so the secret never lands in source control. See postgres for connection string format.
Configure the generator for your runtime
The generator block controls what Prisma emits after prisma generate.
generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
output = "../node_modules/.prisma/client"
}- Use
prisma-client-jsfor Node and Bun. - Add
previewFeaturesonly when you need a specific capability (driver adapters, metrics). Remove them when the feature reaches GA. - Set
outputwhen the defaultnode_modulespath conflicts with a monorepo layout. See prisma-client for the singleton pattern.
Give every model an @id field
Prisma requires exactly one @id or @@id per model.
model Product {
id String @id @default(cuid())
sku String @unique
price Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}- Prefer
cuid()oruuid()overautoincrement()when you distribute writes across multiple databases or replicas. - Use
@updatedAtfor timestamps Prisma should maintain automatically. It sets the field tonow()on everyupdatecall. - Use
@default(now())forcreatedAt. Never compute it in application code.
Declare @unique and @@unique for natural keys
Unique constraints live in the schema, not in a migration file you wrote by hand.
model User {
id String @id @default(cuid())
email String @unique
@@unique([tenantId, slug])
}- Put
@uniqueon single-field natural keys (email, slug, external ID). - Use
@@unique([a, b])for composite uniqueness. Prisma generates the constraint name automatically. - Use
@@index([col])for columns you filter or sort on but do not need unique. See postgres for index sizing rules.
Define relations explicitly on both sides
Prisma requires that you declare the full relation on at least one model.
model Order {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
items OrderItem[]
}
model User {
id String @id @default(cuid())
orders Order[]
}- Specify
fields(the foreign key column),references(the referenced column), andonDeletebehavior. Cascadedeletes children when the parent is deleted.Restrict(the Prisma default) blocks the parent deletion. Choose explicitly rather than relying on defaults.- For self-relations, declare both the scalar and the relation field on the same model.
Set relationMode when your database does not enforce foreign keys
Some hosted databases (PlanetScale, older MySQL, read replicas) do not enforce foreign-key constraints at the engine level.
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
relationMode = "prisma"
}"prisma"emulates foreign-key checks in the Prisma query layer instead of at the DB level."foreignKeys"(default) relies on the database to enforce constraints. Use this with postgres.- Mixing
"prisma"mode with Postgres is legal but redundant. Pick one enforcement layer.
Map Prisma scalar types to native database types
Prisma provides cross-DB scalar types. Use @db.* native type modifiers when the default mapping is too broad.
model Article {
id String @id @default(cuid())
title String @db.VarChar(255)
body String @db.Text
score Float @db.DoublePrecision
meta Json
enabled Boolean @default(true)
}Stringmaps toTEXTby default in Postgres. Use@db.VarChar(n)when length matters for indexes or storage.Intmaps toINTEGER. Use@db.BigIntfor columns that may exceed 2 billion.Jsonmaps toJSONBin Postgres. Queries onJsonfields require$queryRaw. See prisma-raw-queries.- After changing native type modifiers, run
prisma migrate devto emit the correctALTER TABLE. See prisma-migrations.