如何从 Drizzle 迁移到 Prisma ORM
介绍
¥Introduction
本指南将向你展示如何将应用从 Drizzle 迁移到 Prisma ORM。我们将使用基于 Drizzle Next.js 示例 的示例项目来演示迁移步骤。你可以在 GitHub 上找到本指南使用的示例。
¥This guide shows you how to migrate your application from Drizzle to Prisma ORM. We'll use a sample project based off of the Drizzle Next.js example to demonstrate the migration steps. You can find the example used for this guide on GitHub.
你可以在 Prisma ORM 与 Drizzle 页面上了解 Prisma ORM 与 Drizzle 的比较。
¥You can learn how Prisma ORM compares to Drizzle on the Prisma ORM vs Drizzle page.
先决条件
¥Prerequisites
在开始本指南之前,请确保你已准备好:
¥Before starting this guide, make sure you have:
-
你想要迁移的 Drizzle 项目
¥A Drizzle project you want to migrate
-
已安装 Node.js(版本 16 或更高版本)
¥Node.js installed (version 16 or higher)
-
PostgreSQL 或其他受支持的数据库
¥PostgreSQL or another supported database
-
基本了解 Drizzle 和 Next.js
¥Basic familiarity with Drizzle and Next.js
本迁移指南使用 Neon PostgreSQL 作为示例数据库,但它同样适用于任何其他 Prisma ORM 支持 关系数据库。
¥this migration guide uses Neon PostgreSQL as the example database, but it equally applies to any other relational database that are supported by Prisma ORM.
你可以在 Prisma ORM 与 Drizzle 页面上了解 Prisma ORM 与 Drizzle 的比较。
¥You can learn how Prisma ORM compares to Drizzle on the Prisma ORM vs Drizzle page.
迁移过程概述
¥Overview of the migration process
请注意,无论你构建的是哪种应用或 API 层,从 Drizzle 迁移到 Prisma ORM 的步骤始终相同:
¥Note that the steps for migrating from Drizzle to Prisma ORM are always the same, no matter what kind of application or API layer you're building:
-
安装 Prisma CLI
¥Install the Prisma CLI
-
检查你的数据库
¥Introspect your database
-
创建基线迁移
¥Create a baseline migration
-
安装 Prisma 客户端
¥Install Prisma Client
-
逐步用 Prisma 客户端替换 Drizzle 查询
¥Gradually replace your Drizzle queries with Prisma Client
无论你是在构建 REST API(例如,使用 Express、Koa 或 NestJS)、GraphQL API(例如,使用 Apollo Server、TypeGraphQL 或 Nexus),还是任何其他使用 Drizzle 进行数据库访问的应用,这些步骤都适用。
¥These steps apply, no matter if you're building a REST API (e.g. with Express, koa or NestJS), a GraphQL API (e.g. with Apollo Server, TypeGraphQL or Nexus) or any other kind of application that uses Drizzle for database access.
Prisma ORM 非常适合增量采用。这意味着,你无需一次性将整个项目从 Drizzle 迁移到 Prisma ORM,而是可以逐步将数据库查询从 Drizzle 迁移到 Prisma ORM。
¥Prisma ORM lends itself really well for incremental adoption. This means, you don't have migrate your entire project from Drizzle to Prisma ORM at once, but rather you can step-by-step move your database queries from Drizzle to Prisma ORM.
步骤 1。安装 Prisma CLI
¥Step 1. Install the Prisma CLI
采用 Prisma ORM 的第一步是在项目中使用 安装 Prisma CLI:
¥The first step to adopt Prisma ORM is to install the Prisma CLI in your project:
npm install prisma --save-dev && npm install @prisma/client
步骤 2.检查你的数据库
¥Step 2. Introspect your database
2.1.设置 Prisma ORM
¥2.1. Set up Prisma ORM
在检查数据库之前,你需要设置 Prisma 架构 并将 Prisma 连接到数据库。在项目根目录中运行以下命令,创建一个基本的 Prisma 模式文件:
¥Before you can introspect your database, you need to set up your Prisma schema and connect Prisma to your database. Run the following command in the root of your project to create a basic Prisma schema file:
npx prisma init --output ../generated/prisma
此命令为你创建了一个名为 prisma
的新目录,其中包含以下文件:
¥This command created a new directory called prisma
with the following files for you:
-
schema.prisma
:指定数据库连接和模型的 Prisma 模式¥
schema.prisma
: Your Prisma schema that specifies your database connection and models -
.env
:一个dotenv
,用于将数据库连接 URL 配置为环境变量¥
.env
: Adotenv
to configure your database connection URL as an environment variable
你可能已经有一个 .env
文件。如果是这样,prisma init
命令将向其附加行,而不是创建新文件。
¥You may already have a .env
file. If so, the prisma init
command will append lines to it rather than creating a new file.
Prisma 模式目前如下所示:
¥The Prisma schema currently looks as follows:
// 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 扩展 以获得语法高亮、格式化、自动补齐等更多酷炫功能。
¥If you're using VS Code, be sure to install the Prisma VS Code extension for syntax highlighting, formatting, auto-completion and a lot more cool features.
2.2.连接你的数据库
¥2.2. Connect your database
如果你不使用 PostgreSQL,则需要将 datasource
块中的 provider
字段调整为你当前使用的数据库:
¥If you're not using PostgreSQL, you need to adjust the provider
field on the datasource
block to the database you currently use:
- 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 应该可以正常工作。
¥Once that's done, you can configure your database connection URL in the .env
file. Drizzle and Prisma ORM use the same format for connection URLs, so your existing connection URL should work fine.
2.3.使用 Prisma ORM 检查你的数据库
¥2.3. Introspect your database using Prisma ORM
连接 URL 到位后,你可以 introspect 数据库以生成 Prisma 模型:
¥With your connection URL in place, you can introspect your database to generate your Prisma models:
npx prisma db pull
如果你使用的是 示例项目,则会创建以下模型:
¥If you're using the sample project the following model would be created:
model todo {
id Int @id
text String
done Boolean @default(false)
}
生成的 Prisma 模型代表一个数据库表。Prisma 模型是编程式 Prisma 客户端 API 的基础,该 API 允许你向数据库发送查询。
¥The generated Prisma model represents a database table. Prisma models are the foundation for your programmatic Prisma Client API which allows you to send queries to your database.
2.4.创建基线迁移
¥2.4. Create a baseline migration
要继续使用 Prisma Migrate 来改进你的数据库模式,你需要 数据库的基线。
¥To continue using Prisma Migrate to evolve your database schema, you will need to baseline your database.
首先,创建一个 migrations
目录,并在其中添加一个目录,其中包含你用于迁移的首选名称。在此示例中,我们将使用 0_init
作为迁移名称:
¥First, create a migrations
directory and add a directory inside with your preferred name for the migration. In this example, we will use 0_init
as the migration name:
mkdir -p prisma/migrations/0_init
接下来,使用 prisma migrate diff
生成迁移文件。使用以下参数:
¥Next, generate the migration file with prisma migrate diff
. Use the following arguments:
-
--from-empty
:假设你要迁移的数据模型为空¥
--from-empty
: assumes the data model you're migrating from is empty -
--to-schema-datamodel
:使用datasource
块中的 URL 获取当前数据库状态¥
--to-schema-datamodel
: the current database state using the URL in thedatasource
block -
--script
:输出 SQL 脚本¥
--script
: output a SQL script
npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql
检查生成的迁移文件,确保所有内容正确无误。
¥Review the generated migration to ensure everything is correct.
接下来,使用 prisma migrate resolve
和 --applied
参数将迁移标记为已应用。
¥Next, mark the migration as applied using prisma migrate resolve
with the --applied
argument.
npx prisma migrate resolve --applied 0_init
该命令将通过将 0_init
添加到 _prisma_migrations
表来将其标记为已应用。
¥The command will mark 0_init
as applied by adding it to the _prisma_migrations
table.
你现在已经有了当前数据库架构的基线。要对数据库架构进行进一步更改,你可以更新 Prisma 架构并使用 prisma migrate dev
将更改应用到数据库。
¥You now have a baseline for your current database schema. To make further changes to your database schema, you can update your Prisma schema and use prisma migrate dev
to apply the changes to your database.
2.5.调整 Prisma 架构(可选)
¥2.5. Adjust the Prisma schema (optional)
通过自省生成的模型目前完全映射到你的数据库表。在本节中,你将学习如何调整 Prisma 模型的命名以遵循 Prisma ORM 的命名约定。
¥Models that are generated via introspection currently exactly map to your database tables. In this section, you'll learn how you can adjust the naming of the Prisma models to adhere to Prisma ORM's naming conventions.
所有这些调整都是可选的,如果你暂时不想调整任何内容,可以直接跳至下一步。你可以随时返回并进行调整。
¥All of these adjustment are entirely optional and you are free to skip to the next step already if you don't want to adjust anything for now. You can go back and make the adjustments at any later point.
与当前 Drizzle 模型的驼峰命名法不同,Prisma ORM 的命名约定如下:
¥As opposed to the current camelCase notation of Drizzle models, Prisma ORM's naming conventions are:
-
模型名称的 Pascal 大小写
¥PascalCase for model names
-
字段名称使用驼峰式命名
¥camelCase for field names
你可以使用 @@map
和 @map
将 Prisma 模型和字段名称映射到底层数据库中现有的表和列名称,从而调整命名。
¥You can adjust the naming by mapping the Prisma model and field names to the existing table and column names in the underlying database using @@map
and @map
.
以下是如何修改上述模型的示例:
¥Here's an example on how you could modify the model above:
model Todo {
id Int @id
text String
done Boolean @default(false)
@@map("todo")
}
步骤 3.安装并生成 Prisma 客户端
¥Step 3. Install and generate Prisma Client
下一步,你可以在项目中安装 Prisma 客户端,以便开始替换项目中当前使用 Drizzle 进行的数据库查询:
¥As a next step, you can install Prisma Client in your project so that you can start replacing the database queries in your project that are currently made with Drizzle:
npm install @prisma/client
安装后,你需要运行 generate
才能将你的架构反映在 TypeScript 类型和自动补齐中。
¥After installing, you need to run generate
in order to have your schema reflected in TypeScript types and autocomplete.
npx prisma generate
步骤 4.用 Prisma 客户端替换 Drizzle 查询
¥Step 4. Replace your Drizzle queries with Prisma Client
在本节中,我们将基于示例 REST API 项目中的示例路由,展示一些从 Drizzle 迁移到 Prisma 客户端的示例查询。要全面了解 Prisma 客户端 API 与 Drizzle 的区别,请查看 比较页面。
¥In this section, we'll show a few sample queries that are being migrated from Drizzle to Prisma Client based on the example routes from the sample REST API project. For a comprehensive overview of how the Prisma Client API differs from Drizzle, check out the comparison page.
首先,设置 PrismaClient
实例,你将使用该实例从各个路由处理程序发送数据库查询。在 db
目录中创建一个名为 prisma.ts
的新文件:
¥First, to set up the PrismaClient
instance that you'll use to send database queries from the various route handlers. Create a new file named prisma.ts
in the db
directory:
touch db/prisma.ts
现在,实例化 PrismaClient
并从文件中导出它,以便稍后在路由处理程序中使用它:
¥Now, instantiate PrismaClient
and export it from the file so you can use it in your route handlers later:
import { PrismaClient } from '@prisma/client'
export const prisma = new PrismaClient()
4.1.替换 getData
查询
¥4.1. Replacing getData
queries
全栈 Next.js 应用包含多个 actions
函数,包括 getData
函数。
¥The fullstack Next.js app has several actions
including getData
.
getData
操作目前实现如下:
¥The getData
action is currently implemented as follows:
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 实现的相同操作:
¥Here is the same action implemented using Prisma Client:
import { prisma } from "@/db/prisma";
export const getData = async () => {
const data = await prisma.todo.findMany();
return data;
};
4.2.替换 POST
请求中的查询
¥4.2. Replacing queries in POST
requests
示例项目 在 POST
请求期间有四个操作:
¥The sample project has four actions that are utilized during POST
requests:
-
addTodo
:创建新的Todo
记录¥
addTodo
: Creates a newTodo
record -
deleteTodo
:删除现有的Todo
记录¥
deleteTodo
: Deletes an existingTodo
record -
toggleTodo
:在现有Todo
记录上切换布尔值done
字段¥
toggleTodo
: Toggles the booleandone
field on an existingTodo
record -
editTodo
:编辑现有Todo
记录上的text
字段¥
editTodo
: Edits thetext
field on an existingTodo
record
addTodo
addTodo
操作目前实现如下:
¥The addTodo
action is currently implemented as follows:
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 实现的相同操作:
¥Here is the same action implemented using 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
操作目前实现如下:
¥The deleteTodo
action is currently implemented as follows:
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 实现的相同操作:
¥Here is the same action implemented using 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
操作目前实现如下:
¥The ToggleTodo
action is currently implemented as follows:
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 实现的相同操作:
¥Here is the same action implemented using 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 无法编辑布尔字段 "就地",因此必须事先获取记录。
¥Note that Prisma ORM does not have the ability to edit a boolean field "in place", so the record must be fetched before hand.
editTodo
editTodo
操作目前实现如下:
¥The editTodo
action is currently implemented as follows:
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 实现的相同操作:
¥Here is the same action implemented using 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("/");
};
更多
¥More
隐式多对多关系
¥Implicit many-to-many relations
与 Drizzle 不同,Prisma ORM 允许你进行 模型隐式多对多关系。这意味着,你无需在架构中显式管理 关系表(有时也称为 JOIN 表),这是一种多对多关系。以下是 Drizzle 与 Prisma ORM 的比较示例:
¥Unlike Drizzle, Prisma ORM allows you to model many-to-many relations implicitly. That is, a many-to-many relation where you do not have to manage the relation table (also sometimes called JOIN table) explicitly in your schema. Here is an example comparing Drizzle with 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),
});
此架构等效于以下 Prisma 架构:
¥This schema is equivalent to the following 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 模式中,多对多关系通过关系表 PostToCategories
明确建模。
¥In this Prisma schema, the many-to-many relation is modeled explicitly via the relation table PostToCategories
.
通过遵循 Prisma ORM 关系表的约定,关系可以如下所示:
¥By instead adhering to the conventions for Prisma ORM relation tables, the relation could look as follows:
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 客户端 API 更符合人机工程学,更简洁,更易于修改此关系中的记录,因为你拥有从 Post
到 Category
(反之亦然)的直接路径,而无需先遍历 PostToCategories
模型。
¥This would also result in a more ergonomic and less verbose Prisma Client API to modify the records in this relation, because you have a direct path from Post
to Category
(and the other way around) instead of needing to traverse the PostToCategories
model first.
如果你的数据库提供商要求表具有主键,那么你必须使用显式语法,并手动创建带有主键的连接模型。这是因为 Prisma ORM(通过 @relation
表示)使用隐式语法为多对多关系创建的关系表(JOIN 表)没有主键。
¥If your database provider requires tables to have primary keys then you have to use explicit syntax, and manually create the join model with a primary key. This is because relation tables (JOIN tables) created by Prisma ORM (expressed via @relation
) for many-to-many relations using implicit syntax do not have primary keys.
Stay connected with Prisma
Continue your Prisma journey by connecting with our active community. Stay informed, get involved, and collaborate with other developers:
- Follow us on X for announcements, live events and useful tips.
- Join our Discord to ask questions, talk to the community, and get active support through conversations.
- Subscribe on YouTube for tutorials, demos, and streams.
- Engage on GitHub by starring the repository, reporting issues, or contributing to an issue.