升级到 Prisma ORM 7(Upgrade to Prisma ORM 7)

Verified 中级 Intermediate 流程型 Process ⚡ Claude Code 专属 ⚡ Claude Code Optimized
9 min read · 463 lines

Prisma 官方 v7 升级指南:破坏性变更 + 分步迁移 + ESM + 框架适配

升级到 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) 预览功能 已移除

升级步骤概览

  1. 更新包到 v7
  2. 配置 package.json 支持 ESM
  3. 更新 TypeScript 配置
  4. 更新 Schema 生成器块
  5. 创建 prisma.config.ts
  6. 安装并配置驱动适配器
  7. 更新 Prisma Client 导入路径
  8. 更新客户端实例化方式
  9. 移除已弃用的代码(中间件、环境变量等)
  10. 运行 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_modules
  • engineType 已移除,删除所有 engineType 设置

数据源块(Datasource Block):

urldirectUrlshadowDatabaseUrl 现在在 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 秒)
})

参考资源

相关技能 Related Skills