本系列文以製作專案為主軸,紀錄小弟學習React以及GrahQL的過程。主要是記下重點步驟以及我覺得需要記憶的部分,有覺得不明確的地方還請留言多多指教。
已經按Prisma schema生成Prisma Client了,不過資料庫連個影都沒有,有Client的CRUD方法也沒用。
接著就來在專案目錄下建立sqlite 資料庫檔案,並且利用Prisma Migrate工具生成資料庫中的Table。
首先在prisma資料夾底下建立 dev.db檔案,這個檔案的位置要符合schema中的datasource url。
//schema.prisma
datasource db {
provider = "sqlite"
url = "file:./dev.db"
}
要根據Prisma schema生成migration檔案,先多看一眼目前的schema models:
//schema.prisma
model List {
id Int @default(autoincrement()) @id
title String
todos Todo[]
}
model Todo {
id Int @default(autoincrement()) @id
name String
list List @relation(fields: [listId], references: [id])
listId Int
}
預計要生成 List、Todo兩個Table。
產生migration的方法很簡單,執行指令:
npx prisma migrate save --name init --experimental
就會自動生成migration檔案,可以在prisma/migrations資料夾底下找到生成的檔案。
解釋一下指令的參數:
確認migration成功生成後,就應用到資料庫中:
prisma migrate up --experimental
跑成功後就表示資料庫中已經存在對應Schema的Table。
要怎麼確認migration是否成功呢? 可以利用Prisma提供的資料庫檢視與編輯工具,Prisma Studio,這也已經包含在一開始安裝的Prisma套件中了。
執行Prisma Studio:
npx prisma studio
編譯成功後就會自動在瀏覽器上開啟,可以選擇要檢視的Table:
可以看到按照schema的欄位顯示出來,不過有些欄位只是關聯參照而已,不是資料庫實際的欄位,畢竟像list欄位不可能真把整個物件存進去。
想看實際上資料庫有哪些欄位的話,我是用VS Code的SQLite套件,可以直接在VS Code裡顯示table跟執行SQL指令。
可以看到有個_Migration table,這是執行migration時Prisma偷偷建立的,用來記錄每次migration的結果,不管成功或失敗。
若想要新增Model欄位該怎麼操作呢?
跟建立Model時一樣,先編輯Schema:
//schema.prisma
model List {
id Int @default(autoincrement()) @id
title String?
order Int @default(0) //新增order欄位
todos Todo[]
}
model Todo {
id Int @default(autoincrement()) @id
name String
order Int @default(0) //新增order欄位
list List? @relation(fields: [listId], references: [id])
listId Int?
}
接著建立新的migration然後up:
npx prisma migrate save --name add-order --experimental
npx prisma migrate up --experimental
每當schema更新時client也要記得更新:
npx prisma generate
重開studio就能看到table更新了:
Prisma會解析Schema的差異並生成對應的migration,不用在自己編寫對應的migration指令,只要顧好Schema就好,是不是很方便?
想要回到之前的migration階段怎麼辦?
目前官方文件中還沒有相應的功能介紹,不過討論串裡有提到可以試試
npx prisma migrate down --experimental 1
會回到上一個Migration的階段。
待未來更新時會實際介紹這個功能吧。
資料庫裡有對應的Table了,該來實際用用Prisma Client,進行資料庫的存取了。
/*
prisma/seeder/seeder.js
*/
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
//dummy data
const lists = [
{
title: "list1",
todos: [
{ name: "todo1", order: 0 },
{ name: "todo2", order: 1 },
],
order: 0,
},
{
title: "list2",
todos: [
{ name: "todo3", order: 0 },
{ name: "todo4", order: 1 },
{ name: "todo5", order: 2 },
],
order: 1,
},
];
async function main() {
await prisma.list.deleteMany();
await prisma.todo.deleteMany();
for (let i = 0; i < lists.length; i++) {
await prisma.list.create({
data: {
title: lists[i].title,
todos: {
create: lists[i].todos,
},
order: lists[i].order,
},
});
}
}
main()
.catch((e) => {
throw e;
})
.finally(async () => {
await prisma.$disconnect();
});
Prisma Client的使用方式大致上就是:
[prisma client 實例].[目標Table].[CRUD方法]
seeder的步驟:
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
todos: {
create: lists[i].todos,
},
因為todos欄位對應的Todo model,所以可以呼叫create,同時生成Todo資料,並且Prisma會自動按照relation生成外鍵參照,像是這裡會根據建立時的list id,生成 Todo model裡的listId。
順手到package.json中加入seeder的script,方便操作
"seed": "node ./prisma/seeder/seeder.js"
執行:
npm run seed
到Prisma Studio看看結果:
上圖是連續執行兩次seed後的結果,因為每次都會清空表單再添加資料,只有id會繼續累加,所以目前的List有id=3,4兩筆資料,而Todo中的listId也會同步對應List的id資訊。
這個特性被稱作 Nested writes,可以根據Model的關聯在一個指令中同時在複數Table裡新增資料,並自動建立外鍵參照,很方便。
這邊先以seeder試手,嚐嚐Prisma Client的功能,接下來要回到Apollo Server中串接Prisma Client,利用Prisma Client的方法對接資料庫,並重現Trello Clone的功能。