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 出来的项目几乎都是这个套路。