typeorm-transactional icon indicating copy to clipboard operation
typeorm-transactional copied to clipboard

@Transactional not work with interceptor

Open thobui14ig opened this issue 1 year ago • 2 comments

I have multiple insert, delete, and update actions within several interceptors of a controller. I put the @Transactional on the controller, but it doesn't work with the interceptors.

thobui14ig avatar Aug 16 '23 04:08 thobui14ig

@thobui14ig why should it work with interceptors?

galkin avatar Oct 20 '23 03:10 galkin

Hi. Let me explain.

In Nest.js, interceptors, guards and pipes are either invoked before or after the controller function is executed. Not within it. Therefore, if you want to run their code within a transaction, you must use the @Transactional decorator directly within the interceptor/guard/pipe. Here's an example:

@Controller('entities')
export class EntityController {
  @Patch(':id')
  @Transactional()  // <------- 
  @UseInterceptors(CheckIfExistsInterceptor)
  async updateEntity(@Param('id') id: number) {
    await this.entityService.update(id, { name: 'Test' });
    console.log('Updated...');

    return { ok: true };
  }
}
@Injectable()
export class CheckIfExistsInterceptor implements NestInterceptor {
  constructor(
    @InjectRepository(UserEntity)
    private readonly userRepository: Repository<UserEntity>,
  ) {}

  @Transactional() // <------- 
  async intercept(
    context: ExecutionContext,
    next: CallHandler<any>,
  ): Promise<Observable<any>> {
    const { params } = context.switchToHttp().getRequest();

    const isEntityExists = await this.userRepository.findOne({
      where: { id: params.id },
    });
    if (!isEntityExists) {
      return throwError(() => new Error('Entity not found'));
    }

    return next.handle();
  }
}

Also, it's important to note that it's currently not possible to share code between an interceptor/pipe/guard and controller within the same transaction. Even if you specify a Propagation option, there will be separate transactions.

The provided code will produce the following "output":

query: START TRANSACTION -- <--- Interceptor's transaction was started
query: SELECT * FROM entity e WHERE e.id = ? LIMIT 1
query: COMMIT -- <-- Interceptor executed

query: START TRANSACTION -- <--- Controller's transaction was started
Updated
query: COMMIT

Here is a bit more information: https://github.com/Aliheym/typeorm-transactional/issues/1

Aliheym avatar Oct 20 '23 14:10 Aliheym