NestJS v11 + Prisma v7 ESM 迁移配置解析

NestJS v11 CommonJS to ESM

首先是 NestJS v11 确实是支持了 ESM,但并非默认就是 ESM。

package.json --- 声明 ESM 模块

js 复制代码
{
  "type": "module"   // 关键:声明为 ESM 包
}

tsconfig.json --- 编译目标

json 复制代码
{
  "compilerOptions": {
    "target": "ES2022",           // 编译目标
    "module": "ESNext",           // 从 CommonJS 改为 ESNext
    "moduleResolution": "bundler" // 从 node 改为 bundler(重要!)
  }
}

为何 moduleResolution: "bundler" 是必须的?

  • Node.js 的 node 模式不支持 package.json 中的 exports 字段条件导出
  • ESM 要求使用 import 而非 require(),bundler 模式能正确解析这类路径

这里实际还有一个重要问题,如果 moduleResolution 属性不为 "bundler " 那么 import 这里的导入的文件后缀一定要明确写是 .ts or .js。不然实际进行构建还是会报错。

配置变化

diff 复制代码
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
-   "module": "CommonJS",
-   "moduleResolution": "node",
+   "target": "ES2022",
+   "module": "ESNext",
+   "moduleResolution": "bundler",
    "baseUrl": ".",
    "outDir": "dist",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
+   "esModuleInterop": true,
+   "allowSyntheticDefaultImports": true,
+   "strict": true,
+   "skipLibCheck": true,
    "paths": {
      "@/*": ["src/*"]
-   }
+   },
  }

Prisma v5 到 v7 升级

Prisma v7 最大的变化是数据源配置方式

在 v5 版本,数据源 URL 是写在 schema.prisma 文件里的

go 复制代码
datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

这种方式简单直接,但有个问题------不同环境(开发、测试、生产)需要不同的数据库 URL,要么靠环境变量展开,要么靠 CI 脚本动态修改 schema.prisma,都不够灵活。

到了 v7,Prisma 团队决定把这个职责剥离出来:数据源配置不再放在 schema 文件里,而是通过独立的 prisma.config.ts 来管理

ts 复制代码
// prisma.config.ts
import 'dotenv/config'
import type { PrismaConfig } from 'prisma'

export default {
  schema: 'prisma/schema.prisma',
  datasource: {
    url: process.env.DATABASE_URL || 'file:./dev/dev.db',
  },
}

注意:prisma.config.ts 需要放在根路径

这个文件本质上是 Prisma 的全局配置文件,它在 prisma generate 和运行时都会被读取。由于它是一个普通的 TypeScript 文件,你可以:

  1. dotenv 加载环境变量
  2. 根据条件动态决定 URL(比如连接池数量、超时设置)
  3. 在同一个文件里做更多 Prisma 相关的全局配置

与此同时,v7 引入了一个新概念------适配器(Adapter) 。以前 Prisma 的 SQLite 支持是通过内置逻辑实现的,现在则统一走 @prisma/adapter-libsql,底层调用 libsql-client。这让 Prisma 的数据库支持更趋于插件化,理论上未来可以接入更多数据库。

所以升级的实质是:把原来硬编码在 schema 里的数据源 URL 拿出来,放到一个独立的、可以用 TypeScript 逻辑控制的配置文件里,然后通过适配器来建立真正的数据库连接。

依赖变化

diff 复制代码
- "@prisma/client": "^5.12.0"
+ "@prisma/client": "^7.8.0"
+ "@prisma/adapter-libsql": "^7.8.0"
+ "@libsql/client": "^0.17.4"
- "prisma": "^5.12.0"
+ "prisma": "^7.8.0"

关键变更对照(v5 : v7)

关键属性 early access (v6.4+) v7 正式
earlyAccess 必写 已删除
schema 写法 path.join(...) 自由拼 支持相对路径字符串或 path.join
migrate.adapter 已删 ,改 experimental.adapter 或直接 adapter()
engine / directUrl 已删 ,回 schema.prismagenerator
.env 自动加载 ,必须 import 'dotenv/config'
env() 来源 --- prisma/config 导出

NestJs + Prisma

实现 prisma.service

ts 复制代码
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common'
import { PrismaClient } from '@prisma/client'
import { PrismaLibSql } from '@prisma/adapter-libsql'

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
  constructor() {
    // 关键:使用 libsql 适配器创建 PrismaClient
    const adapter = new PrismaLibSql({
      url: process.env.DATABASE_URL || 'file:./dev.db',
    })
    super({ adapter })  // 通过 adapter 选项注入
  }

  async onModuleInit() {
    await this.$connect()
  }

  async onModuleDestroy() {
    await this.$disconnect()
  }
}

注册到 NestJS 全局

common.module.ts

ts 复制代码
@Global()  // 全局模块,所有模块都能 inject PrismaService
@Module({
  providers: [PrismaService],
  exports: [PrismaService],
})
export class CommonModule {}

然后在 app.module.ts 导入

ts 复制代码
@Module({
  imports: [ConfigModule.forRoot(), CommonModule, ...],
})
export class AppModule {}

关键变化点

旧版 (Prisma v5) 新版 (Prisma v7)
new PrismaClient() new PrismaClient({ adapter })
直接传 URL 或靠环境变量 通过 PrismaLibSql 适配器
无需手动调用 $connect 依赖 OnModuleInit 生命周期

新增依赖

json 复制代码
"tsx": "^4.23.0"           // ESM 友好的 TS 执行器
"dotenv": "^17.4.2"        // 环境变量加载
"@nestjs/config": "^4.0.4" // NestJS 配置模块

启动方式变更

diff 复制代码
- "dev": "nest start --watch"
+ "dev": "tsx watch src/main.ts"

原因

  • nest start --watch 在 ESM 模式下存在问题
  • tsx 是支持 ESM 的 TypeScript 执行器,比 ts-node 更适合 ESM

当然使用 ts-node 也可以,这样可以保持原来的 nest start 方式,但是实际上重写的改动量会比 tsc 大。

常见问题排查

症状 解决方案
Cannot use import outside module 确认 package.json"type": "module"
Module not found: @prisma/client 先运行 prisma generate
ExperimentalWarning: --experimental-loader 使用 tsx 而非 Node 原生加载
Prisma URL 配置不生效确认 prisma.config.ts 存在且路径正确
相关推荐
勇往直前plus5 小时前
Vue3(篇一) 核心概念——响应式模板语法与组件基础
前端·javascript·vue.js
咩咩啃树皮5 小时前
第43篇:Vue3计算属性(computed)完全精讲——缓存机制、依赖计算、业务最优解
前端·vue.js·缓存
用户938515635076 小时前
从零搭建 AI 日记助手:用 Milvus 向量数据库 + RAG 让机器读懂你的每一天
javascript·人工智能·全栈
小Ti客栈6 小时前
Spring Boot 集成 Springdoc-OpenAPI 与 Knife4j实现接口文档与可视化调试
java·spring boot·后端
徐小夕7 小时前
花了一周,3亿tokens,我开源了一款 Word 文档智能审查平台,文稿自动质检+可视化分析,告别低效人工审核
前端·算法·github
Ai拆代码的曹操7 小时前
Spring 事务 REQUIRES_NEW 嵌套调用:连接池翻倍的秘密
java·后端·spring
a1117767 小时前
坦克大战3D Three.js 3D (开源项目)
开发语言·javascript·3d
kyriewen8 小时前
别再乱用useEffect了——你写的10个里有8个不该存在
前端·javascript·react.js
Ivanqhz8 小时前
Rust &‘static str浅析
java·前端·javascript·rust
IT_陈寒9 小时前
SpringBoot这个分页坑,我踩了三天才爬出来
前端·人工智能·后端