
NestJS 快速集成 Prisma最佳实践
在 NestJS 中集成 Prisma 的需求太常见了。。。总结一份 Snippet 吧
首先,假设已经有了一个通过 nest-cli 来创建的 NestJS 应用。
接着,安装 prisma 相关依赖:
pnpm add prisma -D
pnpm add @prisma/client
然后,往 package.json
的 scripts 部分新增以下脚本:
{
"scripts": {
// ...
"prisma:generate": "prisma generate",
"prisma:push": "prisma db push",
"postinstall": "prisma generate"
}
}
接着执行:
pnpm dlx prisma init
这样之后就会创建一个叫 prisma
的目录,里面有一个 schema.prisma
文件,还会创建一个 .env
,可以配置 Prisma 的数据库连接地址以及其他环境变量。
然后就是根据自己具体的项目,具体设计一下项目的数据库结构,在 schema.prisma
中写明。设计好之后,可以尝试执行一下 pnpm prisma:push
与 pnpm prisma:generate
,看看有没有数据库有没有正确生成、有没有报错。
接着就是为 Prisma 编写 NestJS 适配器。
在一般的 Node.js 项目中,我们可能直接编写一个 lib/prisma.ts
,里面这样写:
import { PrismaClient } from '@prisma/client'
function prismaClientSingleton() {
return new PrismaClient()
}
declare const globalThis: {
prismaGlobal: ReturnType<typeof prismaClientSingleton>
} & typeof global
const prisma = globalThis.prismaGlobal ?? prismaClientSingleton()
export default prisma
这样就导出了一个全局注册的、单例的 prisma 实例,然后直接 import prisma from '@/lib/prisma'
就能用了。
但是,NestJS 是严格的 MVC IoC/DI 框架,我们需要把整个 prisma 实例封装成一个模块注入到 NestJS 的各个 service 中,才能够正常使用。
上代码:
在你的 NestJS 应用中,创建 src/infra/prisma/prisma.module.ts
和 src/infra/prisma/prisma.service.ts
文件。
// prisma.service.ts
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'
import { PrismaClient } from '@prisma/client'
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
await this.$connect()
}
async onModuleDestroy() {
await this.$disconnect()
}
}
因为 PrismaClient 是一个类,在 NestJS 中我们必须通过依赖注入的形式进行数据库连接,所以我们继承它之后,调用 NestJS 提供的几个生命周期钩子,在被依赖注入的时候调用内部的 $connect
方法,销毁的时候调用 $disconnect
方法,完成数据库的连接。
这里的继承其实是比较巧妙的方法,我们相当于是直接把 PrismaClient 实例单纯加了两个生命周期钩子来触发它的连接、断开连接,其余的使用依然是跟 prisma 一模一样,并且加了一个 @Injectable()
装饰器,使其能够被注入。
// prisma.module.ts
import { Global, Module } from '@nestjs/common'
import { PrismaService } from './prisma.service'
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
module 比较简单,就是将这个 service 暴露出去,让它能够被其他模块进行注入,符合 NestJS 最佳实践。
然后在 app.module.ts
中注册这个 module:
import { Module } from '@nestjs/common'
import { PrismaModule } from './infra/prisma/prisma.module'
@Module({
imports: [
// Infrastructure
// ...
PrismaModule,
// Business
// ...
],
})
export class AppModule {}
最后,如果哪个业务模块的 service 需要操作数据库的,直接按照 NestJS 的依赖注入,在 module 层导入 PrismaModule,然后在对应的 service 中使用构造器注入 PrismaService,就可以跟在普通 Node.js 应用中一样使用 prisma 实例了。