如何從 Drizzle 遷移到 Prisma ORM
簡介
本指南將示範如何將您的應用程式從 Drizzle 遷移到 Prisma ORM。我們將使用基於 Drizzle Next.js 範例 的範例專案來示範遷移步驟。您可以在 GitHub 上找到本指南使用的範例。
您可以在 Prisma ORM 與 Drizzle 的比較頁面上了解 Prisma ORM 與 Drizzle 的比較。
先決條件
開始本指南之前,請確保您已具備
- 您想要遷移的 Drizzle 專案
- 已安裝 Node.js (版本 16 或更高版本)
- PostgreSQL 或另一個支援的資料庫
- 基本熟悉 Drizzle 和 Next.js
本遷移指南使用 Neon PostgreSQL 作為範例資料庫,但同樣適用於 Prisma ORM 支援的任何其他關聯式資料庫。
您可以在 Prisma ORM 與 Drizzle 的比較頁面上了解 Prisma ORM 與 Drizzle 的比較。
遷移流程概述
請注意,無論您建置何種應用程式或 API 層,從 Drizzle 遷移到 Prisma ORM 的步驟始終相同
- 安裝 Prisma CLI
- 內省您的資料庫
- 建立基準遷移
- 安裝 Prisma Client
- 逐步將您的 Drizzle 查詢替換為 Prisma Client
無論您是建置 REST API (例如使用 Express、koa 或 NestJS)、GraphQL API (例如使用 Apollo Server、TypeGraphQL 或 Nexus) 還是任何其他使用 Drizzle 進行資料庫存取的應用程式,這些步驟都適用。
Prisma ORM 非常適合逐步採用。這表示您不必一次將整個專案從 Drizzle 遷移到 Prisma ORM,而是可以逐步將資料庫查詢從 Drizzle 移至 Prisma ORM。
步驟 1. 安裝 Prisma CLI
採用 Prisma ORM 的第一步是在您的專案中安裝 Prisma CLI
npm install prisma --save-dev
步驟 2. 內省您的資料庫
2.1. 設定 Prisma ORM
在您可以內省資料庫之前,您需要設定您的 Prisma schema 並將 Prisma 連接到您的資料庫。在專案的根目錄中執行以下命令以建立基本的 Prisma schema 檔案
npx prisma init
此命令建立了一個名為 prisma
的新目錄,其中包含以下檔案供您使用
schema.prisma
:您的 Prisma schema,指定您的資料庫連線和模型.env
:一個dotenv
,用於將您的資料庫連線 URL 設定為環境變數
您可能已經有一個 .env
檔案。如果是這樣,prisma init
命令會將行附加到其中,而不是建立新檔案。
Prisma schema 目前看起來如下
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
如果您使用 VS Code,請務必安裝 Prisma VS Code 擴充功能 以獲得語法突顯、格式化、自動完成和更多酷炫功能。
2.2. 連接您的資料庫
如果您未使用 PostgreSQL,則需要將 datasource
區塊上的 provider
欄位調整為您目前使用的資料庫
- PostgreSQL
- MySQL
- Microsoft SQL Server
- SQLite
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
datasource db {
provider = "sqlserver"
url = env("DATABASE_URL")
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
完成後,您可以在 .env
檔案中設定您的 資料庫連線 URL。Drizzle 和 Prisma ORM 對於連線 URL 使用相同的格式,因此您現有的連線 URL 應該可以正常運作。
2.3. 使用 Prisma ORM 內省您的資料庫
有了您的連線 URL,您可以內省您的資料庫以產生您的 Prisma 模型
npx prisma db pull
如果您使用 範例專案,則會建立以下模型
model todo {
id Int @id
text String
done Boolean @default(false)
}
產生的 Prisma 模型代表一個資料庫表格。Prisma 模型是您的程式化 Prisma Client API 的基礎,可讓您將查詢傳送到您的資料庫。
2.4. 建立基準遷移
若要繼續使用 Prisma Migrate 來發展您的資料庫 schema,您需要建立資料庫基準。
首先,建立一個 migrations
目錄,並在其中新增一個目錄,其中包含您偏好的遷移名稱。在本範例中,我們將使用 0_init
作為遷移名稱
mkdir -p prisma/migrations/0_init
接下來,使用 prisma migrate diff
產生遷移檔案。使用以下引數
--from-empty
:假設您要從空的資料模型遷移--to-schema-datamodel
:使用datasource
區塊中的 URL 的目前資料庫狀態--script
:輸出 SQL 指令碼
npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql
檢閱產生的遷移以確保一切正確。
接下來,使用 prisma migrate resolve
和 --applied
引數將遷移標記為已套用。
npx prisma migrate resolve --applied 0_init
此命令會將 0_init
標記為已套用,方法是將其新增至 _prisma_migrations
表格。
您現在擁有目前資料庫 schema 的基準。若要對您的資料庫 schema 進行進一步的變更,您可以更新您的 Prisma schema 並使用 prisma migrate dev
將變更套用至您的資料庫。
2.5. 調整 Prisma schema (選用)
透過內省產生的模型目前完全對應到您的資料庫表格。在本節中,您將學習如何調整 Prisma 模型的命名,以符合 Prisma ORM 的命名慣例。
所有這些調整都是完全選用的,如果您現在不想進行任何調整,您可以自由跳到下一步。您可以稍後再回來進行調整。
與目前 Drizzle 模型的 camelCase 標記法相反,Prisma ORM 的命名慣例為
- 模型名稱使用 PascalCase
- 欄位名稱使用 camelCase
您可以使用 @@map
和 @map
將 Prisma 模型和欄位名稱對應到基礎資料庫中現有的表格和欄位名稱,來調整命名。
以下是如何修改上述模型的範例
model Todo {
id Int @id
text String
done Boolean @default(false)
@@map("todo")
}
步驟 3. 安裝並產生 Prisma Client
下一步,您可以在您的專案中安裝 Prisma Client,以便您可以開始替換目前使用 Drizzle 進行的專案中的資料庫查詢
npm install @prisma/client
安裝後,您需要執行 generate
以使您的 schema 反映在 TypeScript 類型和自動完成中。
npx prisma generate
步驟 4. 將您的 Drizzle 查詢替換為 Prisma Client
在本節中,我們將展示一些範例查詢,這些查詢是根據範例 REST API 專案中的範例路由從 Drizzle 遷移到 Prisma Client 的。如需 Prisma Client API 與 Drizzle 有何不同的全面概述,請查看比較頁面。
首先,設定您將用來從各種路由處理程式傳送資料庫查詢的 PrismaClient
實例。在 db
目錄中建立一個名為 prisma.ts
的新檔案
touch db/prisma.ts
現在,實例化 PrismaClient
並從檔案匯出它,以便您稍後可以在路由處理程式中使用它
import { PrismaClient } from '@prisma/client'
export const prisma = new PrismaClient()
4.1. 替換 getData
查詢
完整的 Next.js 應用程式有幾個 actions
,包括 getData
。
getData
動作目前實作如下
import db from "@/db/drizzle";
import { todo } from "@/db/schema";
export const getData = async () => {
const data = await db.select().from(todo);
return data;
};
以下是使用 Prisma Client 實作的相同動作
import { prisma } from "@/db/prisma";
export const getData = async () => {
const data = await prisma.todo.findMany();
return data;
};
4.2. 替換 POST
請求中的查詢
範例專案 有四個在 POST
請求期間使用的動作
addTodo
:建立新的Todo
記錄deleteTodo
:刪除現有的Todo
記錄toggleTodo
:切換現有Todo
記錄上的布林值done
欄位editTodo
:編輯現有Todo
記錄上的text
欄位
addTodo
addTodo
動作目前實作如下
import { revalidatePath } from "next/cache";
import db from "@/db/drizzle";
import { todo } from "@/db/schema";
export const addTodo = async (id: number, text: string) => {
await db.insert(todo).values({
id: id,
text: text,
});
revalidatePath("/");
};
以下是使用 Prisma Client 實作的相同動作
import { revalidatePath } from "next/cache";
import { prisma } from "@/db/prisma";
export const addTodo = async (id: number, text: string) => {
await prisma.todo.create({
data: { id, text },
})
revalidatePath("/");
};
deleteTodo
deleteTodo
動作目前實作如下
import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import db from "@/db/drizzle";
import { todo } from "@/db/schema";
export const deleteTodo = async (id: number) => {
await db.delete(todo).where(eq(todo.id, id));
revalidatePath("/");
};
以下是使用 Prisma Client 實作的相同動作
import { revalidatePath } from "next/cache";
import { prisma } from "@/db/prisma";
export const deleteTodo = async (id: number) => {
await prisma.todo.delete({ where: { id } });
revalidatePath("/");
};
toggleTodo
ToggleTodo
動作目前實作如下
import { eq, not } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import db from "@/db/drizzle";
import { todo } from "@/db/schema";
export const toggleTodo = async (id: number) => {
await db
.update(todo)
.set({
done: not(todo.done),
})
.where(eq(todo.id, id));
revalidatePath("/");
};
以下是使用 Prisma Client 實作的相同動作
import { revalidatePath } from "next/cache";
import { prisma } from "@/db/prisma";
export const toggleTodo = async (id: number) => {
const todo = await prisma.todo.findUnique({ where: { id } });
if (todo) {
await prisma.todo.update({
where: { id: todo.id },
data: { done: !todo.done },
})
revalidatePath("/");
}
};
請注意,Prisma ORM 無法「就地」編輯布林值欄位,因此必須事先擷取記錄。
editTodo
editTodo
動作目前實作如下
import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import db from "@/db/drizzle";
import { todo } from "@/db/schema";
export const editTodo = async (id: number, text: string) => {
await db
.update(todo)
.set({
text: text,
})
.where(eq(todo.id, id));
revalidatePath("/");
};
以下是使用 Prisma Client 實作的相同動作
import { revalidatePath } from "next/cache";
import { prisma } from "@/db/prisma";
export const editTodo = async (id: number, text: string) => {
await prisma.todo.update({
where: { id },
data: { text },
})
revalidatePath("/");
};
更多資訊
隱含多對多關係
與 Drizzle 不同,Prisma ORM 允許您隱含地建立多對多關係模型。也就是說,在多對多關係中,您不必在您的 schema 中明確地管理關係表格 (有時也稱為 JOIN 表格)。以下範例比較 Drizzle 與 Prisma ORM
import { boolean, integer, pgTable, serial, text } from "drizzle-orm/pg-core";
export const posts = pgTable('post', {
id: serial('serial').primaryKey(),
title: text('title').notNull(),
content: text('content'),
published: boolean('published').default(false).notNull(),
});
export const categories = pgTable('category', {
id: serial('serial').primaryKey(),
name: text('name').notNull(),
});
export const postsToCategories = pgTable('posts_to_categories', {
postId: integer('post_id').notNull().references(() => users.id),
categoryId: integer('category_id').notNull().references(() => chatGroups.id),
});
此 schema 等同於以下 Prisma schema
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
postsToCategories PostToCategories[]
@@map("post")
}
model Category {
id Int @id @default(autoincrement())
name String
postsToCategories PostToCategories[]
@@map("category")
}
model PostToCategories {
postId Int
categoryId Int
category Category @relation(fields: [categoryId], references: [id])
post Post @relation(fields: [postId], references: [id])
@@id([postId, categoryId])
@@index([postId])
@@index([categoryId])
@@map("posts_to_categories")
}
在此 Prisma schema 中,多對多關係是透過關係表格 PostToCategories
明確地建模的。
藉由改為遵循 Prisma ORM 關係表格的慣例,關係可以如下所示
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
categories Category[]
}
model Category {
id Int @id @default(autoincrement())
name String
posts Post[]
}
這也會產生更符合人體工學且較不冗長的 Prisma Client API 來修改此關係中的記錄,因為您有從 Post
到 Category
(以及反向) 的直接路徑,而不是需要先遍歷 PostToCategories
模型。
如果您的資料庫供應商要求表格具有主索引鍵,則您必須使用明確的語法,並手動建立具有主索引鍵的聯結模型。這是因為 Prisma ORM 針對使用隱含語法的多對多關係建立的關係表格 (JOIN 表格) (透過 @relation
表示) 沒有主索引鍵。