NestJS中如何无需传递事务绑定Prisma实例实现跨服务事务
解决NestJS中Prisma事务跨服务自动绑定的问题
方案:利用AsyncLocalStorage实现事务上下文传递
1. 创建Prisma上下文服务
封装一个PrismaContextService,通过AsyncLocalStorage存储和获取当前事务绑定的Prisma实例:
import { Injectable } from '@nestjs/common'; import { AsyncLocalStorage } from 'async_hooks'; import { PrismaClient } from '@prisma/client'; @Injectable() export class PrismaContextService { private readonly als = new AsyncLocalStorage<PrismaClient>(); // 将事务绑定的Prisma实例注入当前执行上下文 runWithPrisma<T>(prisma: PrismaClient, callback: () => Promise<T>): Promise<T> { return this.als.run(prisma, callback); } // 获取上下文内的Prisma实例,无上下文时返回默认实例 getPrisma(defaultPrisma: PrismaClient): PrismaClient { return this.als.getStore() ?? defaultPrisma; } }
2. 修改PrismaService,集成上下文能力
改造原PrismaService,让所有操作优先从上下文读取事务绑定的实例:
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; import { PrismaClient } from '@prisma/client'; import { PrismaContextService } from './prisma-context.service'; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { constructor(private readonly prismaContext: PrismaContextService) { super(); // 用动态代理自动拦截模型访问,无需逐个编写getter return new Proxy(this, { get(target, prop) { if (prop in target) { return Reflect.get(target, prop); } const prisma = target.prismaContext.getPrisma(target); return Reflect.get(prisma, prop); }, }) as this; } async onModuleInit() { await this.$connect(); } async onModuleDestroy() { await this.$disconnect(); } // 重写$transaction方法,将事务实例注入上下文 async $transaction<T>(fn: (prisma: PrismaClient) => Promise<T>): Promise<T> { return super.$transaction((tx) => { return this.prismaContext.runWithPrisma(tx, () => fn(tx)); }); } }
3. 其他服务正常调用即可
UserService和OtherService无需修改,内部的Prisma操作会自动读取上下文里的事务实例:
@Injectable() export class UserService { constructor(private readonly prismaService: PrismaService) {} async update(userId: string, data: Partial<User>) { // 若当前处于事务上下文,自动使用事务绑定的实例 return this.prismaService.user.update({ where: { id: userId }, data, }); } }
4. 事务调用保持原有写法
OrdersService中的事务逻辑无需额外传递实例,跨服务操作会自动纳入同一事务:
@Injectable() export class OrdersService { constructor( private readonly prismaService: PrismaService, private readonly userService: UserService, private readonly otherService: OtherService, ) {} async someFn() { return this.prismaService.$transaction(async () => { await this.userService.update('123', { name: 'Updated' }); await this.otherService.delete('456'); }); } }
内容的提问来源于stack exchange,提问作者BravoZulu
相关产品推荐
相关产品推荐

