Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@Transactional not work with interceptor #41

Open
thobui14ig opened this issue Aug 16, 2023 · 2 comments
Open

@Transactional not work with interceptor #41

thobui14ig opened this issue Aug 16, 2023 · 2 comments

Comments

@thobui14ig
Copy link

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.

@galkin
Copy link

galkin commented Oct 20, 2023

@thobui14ig why should it work with interceptors?

@Aliheym
Copy link
Owner

Aliheym commented Oct 20, 2023

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: #1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants