NestJS 快速集成 Prisma最佳实践

NestJS 快速集成 Prisma最佳实践

在 NestJS 中集成 Prisma 的需求太常见了。。。总结一份 Snippet 吧

528字

首先,假设已经有了一个通过 nest-cli 来创建的 NestJS 应用。

接着,安装 prisma 相关依赖:

bash
pnpm add prisma -D
pnpm add @prisma/client

然后,往 package.json 的 scripts 部分新增以下脚本:

json
{
  "scripts": {
    // ...
    "prisma:generate": "prisma generate",
    "prisma:push": "prisma db push",
    "postinstall": "prisma generate"
  }
}

接着执行:

bash
pnpm dlx prisma init

这样之后就会创建一个叫 prisma 的目录,里面有一个 schema.prisma 文件,还会创建一个 .env,可以配置 Prisma 的数据库连接地址以及其他环境变量。

然后就是根据自己具体的项目,具体设计一下项目的数据库结构,在 schema.prisma 中写明。设计好之后,可以尝试执行一下 pnpm prisma:pushpnpm prisma:generate,看看有没有数据库有没有正确生成、有没有报错。

接着就是为 Prisma 编写 NestJS 适配器。

在一般的 Node.js 项目中,我们可能直接编写一个 lib/prisma.ts,里面这样写:

typescript
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.tssrc/infra/prisma/prisma.service.ts 文件。

typescript
// 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() 装饰器,使其能够被注入。

typescript
// 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:

typescript
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 实例了。

评论0