技術2 閱讀

第 3 篇:Prisma Schema 全面拆解

在第 2 篇裡,我們已經用 Prisma 7 + Next.js 16.2.2 + PostgreSQL + pnpm 搭好了一個專案,並且在 prisma/schema.prisma 中定義了一個最小可用的 User 模型。

這一篇的目標是:把這份 schema 從上到下拆開講清楚——datasource、generator、model、enum、各種屬性都是什麼、怎麼用。看完之後,你應該能:


1. 回顧一下我們當前的 schema.prisma

先看一下目前專案裡的 prisma/schema.prisma(第 2 篇的版本):

text
generator client {
  provider = "prisma-client"
  output   = "../generated/prisma"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  createdAt DateTime @default(now())
}

Prisma 官方文件會告訴你:Prisma Schema 一共就三塊

這一篇就是沿著這三個部分展開。


2. datasource:告訴 Prisma 「去哪兒連庫」

2.1 我們當前的 datasource 長什麼樣?

text
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

這段配置的作用是:

結合第 2 篇 .env 的寫法:

text
DATABASE_URL="postgresql://prisma:prisma@localhost:5432/next_prisma_demo?schema=public"

完整含義就是:

2.2 datasource 還能改哪些東西?

在 Prisma Schema Reference 裡,datasource 除了 providerurl,還有一些更高階的配置,例如 relations 模式等,不過你目前用 Postgres,常見配置基本只用這兩個就夠了。

此時你要記住的一句話是:

Prisma 只透過 datasource 知道「你在連什麼資料庫」,並據此決定支援哪些類型、特性。


3. generator:告訴 Prisma 「產生什麼用戶端」

3.1 Prisma 7 推薦的 generator 寫法

在 Prisma 7 中,我們在 schema 裡寫的是:

text
generator client {
  provider = "prisma-client"
  output   = "../generated/prisma"
}

官方文件說明:對於 prisma-client generator,output 是必填,用來告訴 Prisma 把產生的 Client 放到哪裡。

配合第 2 篇的專案結構,這代表:

這就是我們在 lib/prisma.ts 裡這樣導入的原因:

text
import { PrismaClient } from '../generated/prisma'

3.2 generator 的幾個關鍵點

基於 Prisma 7 官方文件,generator 有這些要點值得記:

如果你以後在 monorepo / 多包結構中使用 Prisma,output 會更重要;當前這個單包專案,用預設的 generated/prisma 路徑已經足夠清晰。


4. data model:User 模型裡的每一部分

現在進入最重要部分:資料模型。官方文件稱這一部分為 Data Model Definition,包括 model、enum、attributes 等。

我們當前的 User 模型是:

text
model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  createdAt DateTime @default(now())
}

可以先把它拆成幾個維度來理解:

我們先只看「標量 + 基本屬性」,關係下一篇專講。

4.1 欄位結構:名字 + 類型 + 可選修飾

email 行為例:

text
email     String   @unique

拆開就是:

再看 id 行:

text
id        Int      @id @default(autoincrement())

這正好印證了官方對 model 欄位的定義:欄位 = 名字 + 類型 + 可選修飾符(?、[])+ 屬性

4.2 標量類型(Scalar Types)

Prisma 支援一組標量類型,官方文件列了常用這些:

以及一些 provider-specific 的原生類型映射(透過 @db.* 屬性),比如 @db.VarChar(255)@db.Decimal(10,2) 等。

如果我們要把 User 模型稍微擴展一下,例如加一個餘額欄位,可以寫成:

text
model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  balance   Decimal  @default(0)
  createdAt DateTime @default(now())
}

具體映射到 Postgres 時,可以再加原生類型屬性,例如:

text
balance   Decimal  @db.Decimal(10, 2) @default(0)

這樣 Prisma 就知道應該在資料庫裡用 DECIMAL(10,2)。

4.3 可空與陣列

Prisma 中類型後面的 ?[] 是兩個非常常用的修飾符:

比如我們想讓 name 變成可選欄位:

text
name String?

或者給 User 加一個字串陣列欄位 tags

text
tags String[]

目前我們的 User 是故意保持簡單:所有欄位都必填,暫不使用陣列;後面做關係建模時,你會看到類似 posts Post[] 這樣的陣列類型欄位。


5. 欄位屬性:用 @xxx 給欄位加「約束和語義」

Prisma 用所謂的「屬性(Attribute)」來描述欄位的約束和語義,寫在欄位後面,以 @ 開頭。

User 裡我們已經用到了這些:

5.1 @id:主鍵

text
id Int @id @default(autoincrement())

Prisma 文件說明:ID 欄位唯一識別模型中的每筆記錄,一個 model 可以有一個 @id 或一個 @@id(複合主鍵)。

我們這裡使用的是最常見的「單欄位整數主鍵 + 自增」。

5.2 @default(...):預設值

@default() 可以給欄位指定預設值或預設函式。常見組合:

User 中,我們用的是:

text
id        Int      @id @default(autoincrement())
createdAt DateTime @default(now())

對應資料庫層面就是:id 用序列/自增,createdAt 預設填入插入時的時間。

5.3 @unique:唯一約束

text
email String @unique

表示 User.email 在資料庫中必須唯一。Prisma Migrate 會為此建立一個唯一索引。

你以後在業務裡嘗試插入重複 email 時,會收到類似 「P2002 Unique constraint failed」 的錯誤,這可以用來做使用者註冊衝突驗證。

5.4 @updatedAt:自動更新時間(可選補充)

雖然當前 User 模型裡還沒用,但你基本一定會用到的另一個常見屬性是 @updatedAt

text
updatedAt DateTime @updatedAt

你可以考慮在當前專案裡把 User 模型升級為:

text
model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

6. 模型屬性(@@xxx):在模型層面加規則

模型屬性寫在 model 區塊的底部,不屬於任何欄位,以 @@ 開頭,常見的有:

6.1 @@map:自訂資料表名稱

目前不一定用得上,但理解一下很有用。比如你想在程式碼裡叫 User,資料庫裡資料表名叫 users

text
model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  createdAt DateTime @default(now())

  @@map("users")
}

這樣:

適合「老資料庫接入 + 新程式碼」場景。

6.2 @@index:為常用查詢加索引

假設你經常按 createdAt 查詢最新使用者,可以加一個索引:

text
model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  createdAt DateTime @default(now())

  @@index([createdAt])
}

或者你有一個組合查詢需求,比如常按 (email, createdAt) 組合篩選,也可以這樣寫:

text
@@index([email, createdAt])

Prisma Migrate 會產生對應的索引 SQL,提高查詢效能。


7. enum:固定值集合的更好選擇(預備給 User 增強)

當欄位取值是一個有限集合,比如使用者角色、訂單狀態、任務優先順序等,不建議用隨意字串,官方推薦用 enum。

7.1 為 User 增加 Role 列舉欄位

我們可以在當前 schema 上加一個簡單列舉:

text
enum Role {
  USER
  ADMIN
}

然後在 User 模型中增加一個 role 欄位:

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

好處是:

在程式碼裡會變成類似這樣的體驗:

text
const user = await prisma.user.create({
  data: {
    name: 'Alice',
    email: 'alice@example.com',
    role: 'ADMIN', // 有 TS 類型提示
  },
})

Prisma 官方 Schema 文件建議:凡是「固定集合」的字串欄位,能用 enum 就用 enum,這可以減少拼寫錯誤和魔法字串。


8. 關於關係(@relation):在下一篇用部落格系統專講

在第 2 篇和本篇中,我們刻意只用了單表 User 模型,目的是把 Schema 語言的基礎部分講清楚。關係建模(User–Post–Comment 這種)會在下一篇裡用「部落格系統」的完整例子集中拆解。

這裡先給一個概念圖,和官方文件裡的模型類似:

text
model User {
  id      Int    @id @default(autoincrement())
  name    String
  email   String @unique
  posts   Post[]
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  authorId  Int
  author    User     @relation(fields: [authorId], references: [id])
  createdAt DateTime @default(now())
}

你可以先記住這些點即可:

下一篇會專門圍繞「部落格系統 User / Post / Comment」來講一對一、一對多、多對多和巢狀寫入等內容。


9. 和當前專案的實踐連接:你現在能做什麼?

結合第 2 篇和本篇,你現在已經可以對當前專案的 schema.prisma 做一些穩當的增強,例如:

每次修改 schema 後,你都可以:

  1. pnpm prisma migrate dev --name something 產生遷移並同步本機庫;

  2. 使用 pnpm prisma studio 開啟 UI 看看欄位和資料是否如預期;

  3. 在 Next.js 裡透過 prisma.user.findMany() 等查詢新欄位。

當你之後把 Prisma MCP 接進來時,AI 大部分也就是幫你改這個 schema 和跑這些命令,所以理解 schema 的語義,是你能「審查 AI 輸出」的前提


10. 小結:這一篇你需要真正掌握的點

快速過一下,如果滿足這些,說明你已經掌握了 Prisma Schema 的基礎語法:

SHARE

分享

分享這篇文章。