Search code examples
typescriptnestjstypeorm

Nestjs with Typeorm Transaction in custom repository


I have a custom repository class like this with NestJS/Typeorm:

import { Repository, EntityRepository, getConnection } from 'typeorm';
import { RefreshToken } from './refresh-token.entity';
import { User } from './user.entity';
import { InternalServerErrorException } from '@nestjs/common';

@EntityRepository(RefreshToken)
export class RefreshTokenRepository extends Repository<RefreshToken> {

  async refreshToken({ token, user }: RefreshToken): Promise<RefreshToken> {
    const connection = getConnection();
    const queryRunner = connection.createQueryRunner();

    // establish real database connection using our new query runner
    await queryRunner.connect();

    // lets now open a new transaction:
    await queryRunner.startTransaction();

    try {
      // execute some operations on this transaction:
      await queryRunner.manager.delete(RefreshToken, { token });

      const refreshToken = await queryRunner.manager.save(
        this.buildToken(user),
      );

      // commit transaction now:
      await queryRunner.commitTransaction();

      return refreshToken;
    } catch (err) {
      // since we have errors lets rollback changes we made
      await queryRunner.rollbackTransaction();
    } finally {
      // you need to release query runner which is manually created:
      await queryRunner.release();
    }
  }
}

Is there a different way to make/build a transaction than the one I did in refreshToken() method please? Because getting a connection feels broken and not appropriate with the way NestJS works.

Thanks.


Solution

  • You can use the official integration (package @nestjs/typeorm) in order for your code not to care about getting a connection to the database (official documentation).

    With TypeOrmModule (@nestjs/typeorm), you can create (and import) a dynamic module responsible for establishing a connection to the database.

    @Module({
      imports: [
        TypeOrmModule.forRoot({
          type: 'mysql',
          host: 'localhost',
          port: 3306,
          username: 'root',
          password: 'root',
          database: 'test',
          entities: [],
          synchronize: true,
        }),
      ],
    })
    export class AppModule {}
    

    Then you don't need to care about the connection in the code of your repositories.

    If you already created a connection using "raw" TypeORM (let's say, when your app start), then the Repository already knows how to use it. You can proceed to your request directly, doing something like this:

    try {
      const refreshToken = await this.manager.transaction(async entityManager => {
        await entityManager.delete(RefreshToken, { token });
        return entityManager.save(RefreshToken, this.buildToken(user));
      });
    
      // commit done: use refreshToken here
    } catch (error) {
      // rollback done: handle error here
    }
    

    You cannot start the transaction directly from the repository but you can access (public API) to its manager, which can start a transaction. It provides an entity manager that *must be used for all operations of the transaction.