Prisma ORM + PostgreSQL 全端型別安全資料庫開發:從零到上線的完整指南
說真的,我以前寫後端最怕的就是資料庫那一層。SQL 寫錯一個欄位名稱,要到 runtime 才會爆炸;用傳統 ORM 又覺得抽象層太厚,效能不透明。直到我開始用 Prisma ORM 搭配 PostgreSQL,才真正體會到什麼叫做「型別安全的資料庫存取」。今天就來聊聊 Prisma ORM PostgreSQL TypeSafe 資料庫存取 Next.js 全端開發的完整流程,把我踩過的坑一次分享給你。
為什麼選 Prisma ORM?跟傳統 ORM 差在哪
市面上 Node.js 的 ORM 不少,TypeORM、Sequelize、Drizzle 都有人用。但 Prisma 最讓我心動的點是:它不是用 class 來定義 model,而是用自己的 Schema 語言,然後自動幫你生成完整的 TypeScript 型別。
這代表什麼?當你在寫 prisma.user.findMany() 的時候,IDE 會自動提示你所有可用的欄位、關聯、篩選條件。拼錯字?編譯階段直接報錯,根本不用等到程式跑起來才發現。
- Schema-first 設計:用
.prisma檔案定義資料模型,一目了然 - 自動型別生成:每次
prisma generate都會更新 TypeScript 型別 - Migration 內建:
prisma migrate dev直接搞定資料庫遷移 - Prisma Studio:內建 GUI 管理工具,除錯超方便
專案建置:Next.js + Prisma + PostgreSQL
假設你已經有一個 Next.js 專案,安裝 Prisma 只需要幾個指令:
npm install prisma @prisma/client
npx prisma init --datasource-provider postgresql
這會在專案根目錄產生 prisma/schema.prisma 檔案和 .env 檔案。把你的 PostgreSQL 連線字串填進 .env:
DATABASE_URL="postgresql://user:password@localhost:5432/mydb?schema=public"
定義 Schema:你的資料藍圖
Prisma Schema 的語法非常直覺。來看一個部落格系統的範例:
model User {
id String @id @default(cuid())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
tags Tag[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Tag {
id String @id @default(cuid())
name String @unique
posts Post[]
}
寫完之後跑 npx prisma migrate dev --name init,Prisma 會自動幫你建表、產生 migration 檔案、更新 TypeScript 型別。整個流程不超過 30 秒。
型別安全的 CRUD 操作
這是 Prisma 最爽的部分。我們來看幾個常見操作:
新增資料
const newPost = await prisma.post.create({
data: {
title: 'Prisma 真的很好用',
content: '型別安全讓開發體驗提升好幾個等級',
author: {
connect: { email: '[email protected]' }
},
tags: {
connectOrCreate: [
{ where: { name: 'prisma' }, create: { name: 'prisma' } },
{ where: { name: 'postgresql' }, create: { name: 'postgresql' } }
]
}
},
include: {
author: true,
tags: true
}
})
注意到了嗎?include 裡面放什麼,回傳的型別就會自動包含對應的關聯資料。你不需要手動定義任何 interface,Prisma Client 全部幫你搞定。
查詢與篩選
const posts = await prisma.post.findMany({
where: {
published: true,
author: {
email: { contains: '@example.com' }
},
tags: {
some: { name: 'prisma' }
}
},
orderBy: { createdAt: 'desc' },
take: 10,
select: {
id: true,
title: true,
author: { select: { name: true } }
}
})
用 select 可以精確控制回傳欄位,減少不必要的資料傳輸。而且回傳型別會自動縮窄,只包含你 select 的欄位。這在打造高效能 API 時特別重要,跟Redis 快取策略搭配使用,可以大幅降低資料庫壓力。
在 Next.js 中整合 Prisma
Next.js 的 Server Components 和 Server Actions 跟 Prisma 簡直是天作之合。你可以直接在 Server Component 裡面查詢資料庫,完全不需要另外寫 API:
// app/posts/page.tsx
import { prisma } from '@/lib/prisma'
export default async function PostsPage() {
const posts = await prisma.post.findMany({
where: { published: true },
include: { author: true },
orderBy: { createdAt: 'desc' }
})
return (
<div>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>by {post.author.name}</p>
</article>
))}
</div>
)
}
不過有個坑要注意:在 development 環境下,Next.js 的 hot reload 會不斷重新建立 Prisma Client 實例,導致連線數爆掉。標準解法是用 global singleton:
// lib/prisma.ts
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
進階功能:讓你的資料層更強大
TypedSQL:寫原生 SQL 也有型別
Prisma 5.x 推出的 TypedSQL 功能超級實用。當你遇到複雜查詢,Prisma Client API 搞不定的時候,可以寫 .sql 檔案放在 prisma/sql/ 資料夾裡,Prisma 會自動幫你生成帶型別的函式:
-- prisma/sql/getPostStats.sql
SELECT
a.name AS author_name,
COUNT(p.id)::int AS post_count,
MAX(p."createdAt") AS latest_post
FROM "User" a
LEFT JOIN "Post" p ON p."authorId" = a.id
GROUP BY a.id, a.name
ORDER BY post_count DESC
然後在程式裡這樣用:const stats = await prisma.$queryRawTyped(getPostStats()),回傳值自動帶有正確型別。
Middleware 與擴展
Prisma 支援 middleware,可以在查詢前後加入自訂邏輯,例如 soft delete、查詢日誌、效能監控等。如果你的系統有跨服務的需求,可以考慮搭配Kubernetes Gateway API 來管理微服務之間的流量。
部署注意事項
部署到 Vercel 或其他 serverless 平台時,有幾個重點:
- Connection Pooling:Serverless 環境必須用連線池,推薦用 Prisma Accelerate 或 PgBouncer
- Migration:CI/CD 中記得跑
prisma migrate deploy(不是dev) - Generate:Build 時確保先跑
prisma generate,通常放在postinstallscript - Edge Runtime:如果用到Cloudflare Workers 等邊緣運算環境,需要改用 Prisma 的 edge-compatible driver adapter
效能優化小技巧
用了 Prisma 半年多,以下幾個技巧我覺得最實用:
- 善用 select:不要每次都 include 所有關聯,只拿你需要的欄位
- 批次操作:用
createMany、updateMany取代迴圈裡的單筆操作 - 索引策略:在 Schema 裡用
@@index定義常用查詢的索引 - 查詢日誌:開啟
log: ['query']來監控慢查詢 - 關聯預載:避免 N+1 問題,善用
include做 eager loading
結語:型別安全不是奢侈品
回頭看,Prisma ORM PostgreSQL TypeSafe 資料庫存取 Next.js 全端開發這套組合,已經成為我現在每個新專案的標配。它不只是讓寫 code 變快,更重要的是讓你對資料層有信心——改了 Schema 之後,哪裡會壞掉,TypeScript 編譯器會立刻告訴你。
如果你還在用字串拼接 SQL 或是沒有型別保護的 ORM,真的建議試試看 Prisma。那種「改了 Schema 之後全專案自動更新型別」的體驗,用過就回不去了。
繼續閱讀
SQLite Limbo:用 Async Rust 打造的下一代分散式嵌入式資料庫
SQLite Limbo 是一個以 Async Rust 從零重寫的 SQLite 相容嵌入式資料庫,專為邊緣運算、Serverless 與分散式系統設計。
相關文章
你可能也喜歡
探索其他領域的精選好文