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 文件,你可以:
- 用
dotenv加载环境变量 - 根据条件动态决定 URL(比如连接池数量、超时设置)
- 在同一个文件里做更多 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.prisma 的 generator 块 |
.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 存在且路径正确 |