在Day14中,我們使用了Next.jsRouteHandlers與Zod建立了具備強型別防禦與統一Response格式的API端點。然而,當時API底層依然是用記憶體陣列(skillsDb)來模擬資料寫入。
今天(Day15),我們將正式告別記憶體陣列,踏入真實資料庫Persistence(持久化)的領域!
我們將選擇Node.js/TypeScript生態系中最受歡迎的ORM套件——Prisma,搭配PostgreSQL資料庫,引導AI進行資料庫Schema建模、關聯宣告、資料型別映射,並執行資料庫Migration!
1.為什麼選擇Prisma+PostgreSQL?
在VibeCoding的架構中,PrismaORM擁有幾項無可比擬的優勢:
單一真理來源(SingleSourceofTruth):只需要在schema.prisma中定義一次模型,Prisma就會自動生成完整的TypeScriptClient、資料庫MigrationSQL與型別宣告。
與Zod/TypeScript完美契合:PrismaClient生成的型別可以直接與我們在Day14寫的Zod驗證層與前端Component共享。
直觀的聲明式語法(DeclarativeSchema):連LLMAI都極度擅長撰寫與重構.prisma檔案。
實戰步驟1:安裝Prisma與初始化資料庫設定
步驟A:安裝套件
在Terminal執行:
npm install @prisma/client
npm install -D prisma
步驟B:初始化Prisma
執行以下指令產生prisma/schema.prisma與.env檔:
npx prisma init
這會在.env中產生資料庫連線字串範例:
DATABASE_URL="postgresql://postgres:password@localhost:5432/vibepulse?schema=public"
3.實戰步驟2:請AI撰寫prisma/schema.prisma
開啟Cursor,對著@prisma/schema.prisma發送Prompt:
@lib/validations/skill.ts(參考型別與枚舉宣告)
@mocks/skillsData.ts(參考原始資料結構)
你是一位資深的DatabaseArchitect。請幫我為VibePulse設計`prisma/schema.prisma`。
需求細節:
1.Datasource&Generator:
-provider="postgresql"
-generatorclient="prisma-client-js"
2.Enum定義:
-`Category`:`FRONTEND`,`BACKEND`,`AI`,`DEVOPS`
-`Status`:`LEARNING`,`MASTERED`,`PLANNED`
3.SkillModel(資料表`skills`):
-`id`:String(UUID,`@id@default(uuid())`)
-`title`:String
-`category`:Category(Enum)
-`proficiency`:Int(`@default(0)`)
-`tags`:String[](PostgreSQL原生Array支援)
-`status`:Status(`@default(LEARNING)`)
-`notesCount`:Int(`@default(0)`)
-`lastPracticedAt`:DateTime(`@default(now())`)
-`createdAt`:DateTime(`@default(now())`)
-`updatedAt`:DateTime(`@updatedAt`)
4.效能優化(Indexes):
-為`category`與`status`欄位建立單獨或複合索引(`@@index`),以加速未來的查詢效能。
-為`@map("skills")`設定資料表底層映射名稱。
AI產出的prisma/schema.prisma:
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum Category {
FRONTEND
BACKEND
AI
DEVOPS
}
enum Status {
LEARNING
MASTERED
PLANNED
}
model Skill {
id String @id @default(uuid())
title String
category Category
proficiency Int @default(0)
tags String[] @default([])
status Status @default(LEARNING)
notesCount Int @default(0)
lastPracticedAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([category])
@@index([status])
@@map("skills")
}
4.實戰步驟3:執行Migration與建立資料庫單例(lib/prisma.ts)
步驟A:執行首次Migration
確保PostgreSQL資料庫啟動後,在Terminal執行:
npx prisma migrate dev --name init_skill_table
Prisma會自動在prisma/migrations目錄下生成標準SQL並建置skills資料表!
步驟B:建立PrismaClient單例(Singleton)
在Next.js的開發環境(HMR熱重載)中,如果不將PrismaClient寫成單例,可能會因為多次instantiation導致資料庫連線池(ConnectionPool)耗盡。
請AI建立@lib/prisma.ts:
// lib/prisma.ts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
});
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
5.實戰步驟4:寫入SeedData(將MockData遷移至PostgreSQL)
為了讓資料庫一開始就有資料測試,我們請AI撰寫一隻SeedScriptprisma/seed.ts:
@mocks/skillsData.ts
@prisma/schema.prisma
請幫我撰寫 `prisma/seed.ts` 腳本,將 `mocks/skillsData.ts` 中的假資料寫入 PostgreSQL 資料庫。
需求細節:
1. 先執行 `prisma.skill.deleteMany()` 清空舊資料。
2. 將 `mockSkills` 的字串 Enum (如 'Frontend') 轉譯為 Schema 中的的大寫 Enum (如 'FRONTEND')。
3. 使用 `prisma.skill.createMany()` 批量寫入。
4. 在 `package.json` 中加入 `"prisma": { "seed": "ts-node prisma/seed.ts" }` 的設定指引。
執行Seed腳本:
npx prisma db seed
Terminal顯示:
Seeding database...
Created 5 skills in PostgreSQL successfully!
今天我們完成了VibePulse資料持久化架構的重磅升級:
使用聲明式語法完成了prisma/schema.prisma的PostgreSQL建模。
建立了完整的Enum、Index索引與Migration檔案。
封裝了避免連線洩漏的PrismaClientSingleton,並完成SeedData導入!