Prisma 中文文档 下载 App

NestJS + Prisma 模块化集成

PrismaService 作为 Provider

NestJS 的依赖注入体系非常适合把 Prisma Client 封装成一个可注入的 Provider:

// src/prisma/prisma.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy } 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 社区最常见的做法——这样 PrismaService 实例上直接有 this.user.findMany() 等所有 Prisma Client 方法。

PrismaModule 全局注册

// src/prisma/prisma.module.ts
import { Global, Module } from '@nestjs/common'
import { PrismaService } from './prisma.service'

@Global()
@Module({
  providers: [PrismaService],
  exports: [PrismaService],
})
export class PrismaModule {}

加 @Global() 之后,在 AppModule 里导入一次,所有子模块都能注入 PrismaService,不需要每个模块都 import。

// src/app.module.ts
import { Module } from '@nestjs/common'
import { PrismaModule } from './prisma/prisma.module'
import { UsersModule } from './users/users.module'

@Module({
  imports: [PrismaModule, UsersModule],
})
export class AppModule {}

Service 层注入

// src/users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common'
import { PrismaService } from '../prisma/prisma.service'
import { CreateUserDto } from './dto/create-user.dto'

@Injectable()
export class UsersService {
  constructor(private readonly prisma: PrismaService) {}

  async findAll() {
    return this.prisma.user.findMany()
  }

  async findOne(id: number) {
    const user = await this.prisma.user.findUnique({ where: { id } })
    if (!user) throw new NotFoundException('用户不存在')
    return user
  }

  async create(dto: CreateUserDto) {
    return this.prisma.user.create({ data: dto })
  }

  async update(id: number, dto: Partial<CreateUserDto>) {
    try {
      return await this.prisma.user.update({ where: { id }, data: dto })
    } catch {
      throw new NotFoundException('用户不存在')
    }
  }

  async remove(id: number) {
    try {
      await this.prisma.user.delete({ where: { id } })
    } catch {
      throw new NotFoundException('用户不存在')
    }
  }
}

Prisma 错误码 → NestJS 异常

// src/prisma/prisma-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common'
import { Prisma } from '@prisma/client'
import { Response } from 'express'

@Catch(Prisma.PrismaClientKnownRequestError)
export class PrismaExceptionFilter implements ExceptionFilter {
  catch(exception: Prisma.PrismaClientKnownRequestError, host: ArgumentsHost) {
    const ctx = host.switchToHttp()
    const response = ctx.getResponse<Response>()

    let status = HttpStatus.INTERNAL_SERVER_ERROR
    let message = '内部错误'

    switch (exception.code) {
      case 'P2002':
        status = HttpStatus.CONFLICT
        message = '唯一约束冲突'
        break
      case 'P2025':
        status = HttpStatus.NOT_FOUND
        message = '记录不存在'
        break
      case 'P2003':
        status = HttpStatus.BAD_REQUEST
        message = '外键约束失败'
        break
    }

    response.status(status).json({ statusCode: status, message })
  }
}

在 main.ts 里全局注册:

import { NestFactory } from '@nestjs/core'
import { PrismaExceptionFilter } from './prisma/prisma-exception.filter'

async function bootstrap() {
  const app = await NestFactory.create(AppModule)
  app.useGlobalFilters(new PrismaExceptionFilter())
  await app.listen(3000)
}
bootstrap()

NestJS Guard + Prisma 权限查询

// src/auth/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'
import { PrismaService } from '../prisma/prisma.service'

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private readonly prisma: PrismaService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest()
    const userId = request.headers['x-user-id']

    if (!userId) return false

    const user = await this.prisma.user.findUnique({
      where: { id: Number(userId) },
      select: { role: true },
    })

    return user?.role === 'ADMIN'
  }
}

Controller 示例

// src/users/users.controller.ts
import { Controller, Get, Post, Body, Param, Put, Delete, UseGuards } from '@nestjs/common'
import { UsersService } from './users.service'
import { CreateUserDto } from './dto/create-user.dto'
import { RolesGuard } from '../auth/roles.guard'

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  findAll() {
    return this.usersService.findAll()
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(+id)
  }

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto)
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() dto: Partial<CreateUserDto>) {
    return this.usersService.update(+id, dto)
  }

  @Delete(':id')
  @UseGuards(RolesGuard)
  remove(@Param('id') id: string) {
    return this.usersService.remove(+id)
  }
}

总结

NestJS + Prisma 的标准写法就是:封装成 PrismaService extends PrismaClient → 全局 Module 导出 → Service 层注入使用 → ExceptionFilter 统一错误处理。这是社区最主流的模式,你在 GitHub 上搜 nestjs prisma example 出来的项目几乎都是这个套路。