升级到 Prisma ORM 7(Upgrade to Prisma ORM 7)
概述
从 Prisma ORM v6 升级到 v7 的完整迁移指南。此次升级引入了重大破坏性变更,包括仅支持 ESM、必须使用驱动适配器(Driver Adapter)和全新的配置系统。
适用场景
- 从 Prisma v6 升级到 v7
- 更新到
prisma-client生成器 - 设置驱动适配器
- 配置
prisma.config.ts - 修复升级后的导入错误
重要注意事项
- MongoDB 在 v7 中尚不支持 — 请继续使用 v6
- Node.js 20.19.0+ 必需
- TypeScript 5.4.0+ 必需
破坏性变更总览
| 变更项 | v6 | v7 |
|---|---|---|
| 模块格式 | CommonJS | 仅 ESM |
| 生成器提供者(Generator Provider) | prisma-client-js |
prisma-client |
| 输出路径(Output Path) | 自动(node_modules) | 必须显式指定 |
| 驱动适配器(Driver Adapters) | 可选 | 必需 |
| 配置文件 | .env + schema |
prisma.config.ts |
| 环境变量加载 | 自动 | 手动(dotenv) |
| 中间件(Middleware) | $use() |
客户端扩展(Client Extensions) |
| 指标(Metrics) | 预览功能 | 已移除 |
升级步骤概览
- 更新包到 v7
- 配置 package.json 支持 ESM
- 更新 TypeScript 配置
- 更新 Schema 生成器块
- 创建 prisma.config.ts
- 安装并配置驱动适配器
- 更新 Prisma Client 导入路径
- 更新客户端实例化方式
- 移除已弃用的代码(中间件、环境变量等)
- 运行 generate 并测试
快速升级命令
# 更新包
npm install @prisma/client@7
npm install -D prisma@7
# 安装驱动适配器(以 PostgreSQL 为例)
npm install @prisma/adapter-pg
# 安装 dotenv 用于环境变量加载
npm install dotenv
# 重新生成 Client
npx prisma generate
分步迁移指南
步骤 1:配置 ESM 支持
package.json:
{
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}
tsconfig.json:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"target": "ES2023",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "dist"
},
"include": ["src/**/*", "prisma/**/*"]
}
替代方案:Node16/NodeNext
{
"compilerOptions": {
"module": "Node16",
"moduleResolution": "Node16",
"target": "ES2022"
}
}
注意:使用
moduleResolution: "Node16"时,导入路径需要添加.js扩展名:import { helper } from './utils/helper.js'使用
moduleResolution: "bundler"时,扩展名可选。
步骤 2:更新 Schema
// 之前(v6)
generator client {
provider = "prisma-client-js"
}
// 之后(v7)
generator client {
provider = "prisma-client"
output = "../generated"
}
关键变化:
- 提供者从
prisma-client-js改为prisma-client output字段现在是必需的 — Client 不再生成到node_modulesengineType已移除,删除所有engineType设置
数据源块(Datasource Block):
url、directUrl 和 shadowDatabaseUrl 现在在 prisma.config.ts 中配置,Schema 中只保留 provider:
datasource db {
provider = "postgresql"
// URL 在 prisma.config.ts 中配置
}
输出路径示例:
// 标准项目
output = "../generated" // 创建 generated/client
// Monorepo
output = "../../packages/database/generated"
// 与 Schema 同目录
output = "./generated" // 创建 prisma/generated/client
步骤 3:创建 prisma.config.ts
import 'dotenv/config'
import { defineConfig, env } from 'prisma/config'
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations',
},
datasource: {
url: env('DATABASE_URL'),
directUrl: env('DIRECT_URL'),
shadowDatabaseUrl: env('SHADOW_DATABASE_URL'),
},
})
步骤 4:安装驱动适配器
# PostgreSQL
npm install @prisma/adapter-pg
# MySQL
npm install @prisma/adapter-mariadb mariadb
# SQLite
npm install @prisma/adapter-better-sqlite3
# Prisma Postgres
npm install @prisma/adapter-ppg @prisma/ppg
# Neon
npm install @prisma/adapter-neon
步骤 5:更新客户端实例化
// 之前(v6)
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
// 之后(v7)
import { PrismaClient } from '../generated/client'
import { PrismaPg } from '@prisma/adapter-pg'
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL
})
const prisma = new PrismaClient({ adapter })
步骤 6:更新 .gitignore
generated
步骤 7:运行迁移和生成
npx prisma generate
npx prisma migrate dev # 如果需要
已移除功能的迁移
客户端中间件 → 客户端扩展
// 已移除(v6)
prisma.$use(async (params, next) => {
const before = Date.now()
const result = await next(params)
console.log(`Query took ${Date.now() - before}ms`)
return result
})
// 替代方案(v7)— 使用客户端扩展
const prisma = new PrismaClient({ adapter }).$extends({
query: {
$allModels: {
async $allOperations({ operation, model, args, query }) {
const before = Date.now()
const result = await query(args)
console.log(`${model}.${operation} took ${Date.now() - before}ms`)
return result
},
},
},
})
软删除模式
const prisma = new PrismaClient({ adapter }).$extends({
query: {
user: {
async delete({ args, query }) {
// 将 delete 转换为软删除
return prisma.user.update({
where: args.where,
data: { deletedAt: new Date() },
})
},
async findMany({ args, query }) {
// 过滤掉已软删除的记录
args.where = { ...args.where, deletedAt: null }
return query(args)
},
},
},
})
指标(Metrics)→ 自定义计数器
// 已移除
const metrics = await prisma.$metrics.json()
// 替代方案 — 使用扩展
let totalQueries = 0
const prisma = new PrismaClient({ adapter }).$extends({
client: {
async $totalQueries() {
return totalQueries
},
},
query: {
$allModels: {
async $allOperations({ query, args }) {
totalQueries += 1
return query(args)
},
},
},
})
const count = await prisma.$totalQueries()
rejectOnNotFound → OrThrow 方法
// 已移除
const prisma = new PrismaClient({ rejectOnNotFound: true })
// 替代方案 — 使用 OrThrow 方法
const user = await prisma.user.findUniqueOrThrow({
where: { id: 1 },
})
const user = await prisma.user.findFirstOrThrow({
where: { email: 'test@example.com' },
})
已移除的 CLI 标志
| 已移除 | 说明 |
|---|---|
--skip-generate(migrate dev, db push) |
v7 中不再自动运行 generate,显式运行 prisma generate |
--skip-seed(migrate dev, migrate reset) |
v7 中不再自动运行 seed,显式运行 prisma db seed |
--schema, --url(db execute) |
在 prisma.config.ts 中配置 |
migrate diff 选项变更
| 已移除 | 替代 |
|---|---|
--from-url |
--from-config-datasource |
--to-url |
--to-config-datasource |
--from-schema-datasource |
--from-config-datasource |
--to-schema-datasource |
--to-config-datasource |
--shadow-database-url |
在 prisma.config.ts 中配置 |
框架适配
Next.js
确保 next.config.js 改为 next.config.mjs:
// next.config.mjs
export default {
// 配置
}
Express
更新入口文件:
// index.js(需要 "type": "module")
import express from 'express'
import { PrismaClient } from '../generated/client'
const app = express()
const prisma = new PrismaClient()
Jest
配置 Jest 支持 ESM:
{
"jest": {
"preset": "ts-jest/presets/default-esm",
"extensionsToTreatAsEsm": [".ts"],
"transform": {
"^.+\\.tsx?$": ["ts-jest", { "useESM": true }]
}
}
}
或使用原生支持 ESM 的 Vitest。
单例模式(Singleton Pattern)
// lib/prisma.ts
import { PrismaClient } from '../generated/client'
import { PrismaPg } from '@prisma/adapter-pg'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!
})
export const prisma = globalForPrisma.prisma ?? new PrismaClient({ adapter })
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma
}
故障排查
"Cannot find module" 错误
- 检查 generator 块中的
output路径是否与导入路径匹配 - 确保
prisma generate已成功运行
"ERR_REQUIRE_ESM" 错误
- 代码使用了
require()加载 ESM 模块,需要改用import
"Cannot use import statement outside a module" 错误
- 在 package.json 中添加
"type": "module"
SSL 证书错误
- 添加
ssl: { rejectUnauthorized: false }到适配器配置 - 或正确配置 SSL 证书:
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL,
ssl: {
ca: fs.readFileSync('/path/to/ca-cert.pem'),
rejectUnauthorized: true
}
})
连接超时问题
- 驱动适配器使用不同的连接池默认值
- 显式配置连接池设置:
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL,
max: 10, // 最大连接数
idleTimeoutMillis: 30000, // 空闲超时
connectionTimeoutMillis: 5000, // 连接超时(v6 默认 5 秒)
})