Prisma Schema Guide

Datasource & Generator

// schema.prisma

datasource db {
  provider = "postgresql"   // postgresql | mysql | sqlite | sqlserver | mongodb
  url      = env("DATABASE_URL")
  // directUrl = env("DIRECT_URL")  // for connection pooling (e.g. PgBouncer)
}

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["fullTextSearch", "multiSchema"]
  binaryTargets   = ["native", "linux-musl"]  // for Docker
}

// Multiple schemas (PostgreSQL, preview feature)
generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["multiSchema"]
}
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
  schemas  = ["public", "auth"]
}

Model Definition

model User {
  id        Int      @id @default(autoincrement())
  uuid      String   @unique @default(uuid())
  email     String   @unique
  name      String?
  role      Role     @default(USER)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  posts    Post[]
  profile  Profile?

  @@index([email, role])
  @@map("users")   // map to different table name
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String   @db.VarChar(255)
  body      String   @db.Text
  published Boolean  @default(false)
  authorId  Int
  author    User     @relation(fields: [authorId], references: [id], onDelete: Cascade)
  tags      Tag[]    @relation("PostTags")
  createdAt DateTime @default(now())

  @@index([authorId])
  @@index([published, createdAt])
}

enum Role {
  USER
  ADMIN
  MODERATOR
}

Relations

// One-to-One
model User    { profile  Profile?  }
model Profile { user     User    @relation(fields:[userId], references:[id])
                userId   Int     @unique }

// One-to-Many
model User  { posts    Post[]  }
model Post  { author   User    @relation(fields:[authorId], references:[id])
              authorId Int     }

// Many-to-Many (implicit: Prisma creates join table)
model Post { tags Tag[]  }
model Tag  { posts Post[] }

// Many-to-Many (explicit join table)
model Post       { tagLinks PostTag[] }
model Tag        { postLinks PostTag[] }
model PostTag    {
  post     Post   @relation(fields:[postId], references:[id])
  postId   Int
  tag      Tag    @relation(fields:[tagId], references:[id])
  tagId    Int
  addedAt  DateTime @default(now())

  @@id([postId, tagId])
}

// Self-relation (employees / managers)
model Employee {
  id         Int        @id @default(autoincrement())
  managerId  Int?
  manager    Employee?  @relation("manages", fields:[managerId], references:[id])
  reports    Employee[] @relation("manages")
}

Field Attributes

AttributeDescription
@idPrimary key
@uniqueUnique constraint
@default(val)Default value (now(), uuid(), cuid(), autoincrement())
@updatedAtAuto-set on every update
@relationDefine FK and cascades
@map("col")Map field to different column name
@db.VarChar(n)Database-specific type modifier
@@id([a,b])Composite primary key
@@unique([a,b])Composite unique constraint
@@index([a,b])Composite index
@@map("tbl")Map model to different table name

Prisma Client Queries

import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();

// findMany with filter, include, pagination
const users = await prisma.user.findMany({
  where:   { role: "ADMIN", email: { contains: "@example.com" } },
  include: { posts: { where: { published: true }, take: 5 } },
  orderBy: { createdAt: "desc" },
  skip:    0,
  take:    20
});

// create with nested write
await prisma.user.create({
  data: {
    email: "alice@example.com",
    posts: { create: [{ title: "Hello World", body: "..." }] }
  }
});

// transaction
await prisma.$transaction([
  prisma.account.update({ where:{id:1}, data:{balance:{decrement:100} } }),
  prisma.account.update({ where:{id:2}, data:{balance:{increment:100} } })
]);