Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

291
Vistas
What is the correct way to work with transactions with NestJs and TypeORM?

I am using NestJS with TypeORM. I have several modules and each module contain service. There are some modules that import other modules and use their services.

// In Person Module
@Injectable()
class UserService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>
  ) {}

  public async deleteUsersByCompany(companyId: string) {
    const usersToDelete = await this.usersRepository
      .createQueryBuilder('user')
      .where('user.companyId = :companyId', { companyId })
      .getMany();

    if (usersToDelete.length > 0) {
      await this.usersRepository.delete(
        usersToDelete.map((user) => user.id),
      );
    }
  }

  ...
}

// Company Module

@Injectable()
class CompanyService {
  constructor(
    @InjectRepository(Company)
    private companysRepository: Repository<Company>,
    private usersService: UserService
  ) {}

  public async deleteCompany(companyId: string) {
    const companyToDelete = await this.companysRepository.find(companyId);

    if (companyToDelete) {
      await usersService.deleteUsersByCompany(companyToDelete.id)
      await this.companysRepository.delete(companyToDelete);
    }
  }

  ...
}

In this example:

  1. I want usersService.deleteUsersByCompany to be atomic operation
  2. I want companyService.deleteCompany to be atomic operation
  3. Both usersService.deleteUsersByCompany and companyService.deleteCompany can be triggered by the user

I want to wrap both actions in transaction. I've read NestJS documentation and TypeOrm documentation but couldn't find an answer how to do this properly.
The main thing here is that usersService.deleteUsersByCompany is being called by companyService.deleteCompany.

  1. How can I support such "nested transactions"?
  2. Does QueryRunner replace the Repository pattern?
  3. Should my services hold both Repository and Connection instances? Repository for non-transactional operations and connection to get QueryRunner?
over 4 years ago · Santiago Trujillo
1 Respuestas
Responde la pregunta

0

The easiest way to use transaction is using the EntityManager API, check a simple example from a microservice:

import { WRITE_CONNECTION } from '@my-api/common';
import { Injectable, Logger } from '@nestjs/common';
import { RpcException } from '@nestjs/microservices';
import { InjectEntityManager } from '@nestjs/typeorm';
import { EntityManager } from 'typeorm';

@Injectable()
export class MyService {
  private logger = new Logger(MyService.name);
  constructor(
    @InjectEntityManager(WRITE_CONNECTION) private entityManager: EntityManager,
  ) {}

  async saveSomething(data: string): Promise<void> {
    try {
      return await this.entityManager.transaction(async (entityManager) => {
        const firstRepository = entityManager.getCustomRepository(FirstRepository);
        const secondRepository = entityManager.getCustomRepository(SecondRepository);

        const firstRecord = firstRepository.create({ data });
        await firstRepository.save(firstRecord);

        const secondRecord = secondRepository.create({ data });
        await secondRepository.save(secondRecord);

        // Save entities directly
        await entityManager.save([...]);
      });
    } catch (error) {
      this.logger.error(`Failed saving something, error ${error.message}`, error.stack);
      throw new RpcException(error.message);
    }
  }
}

As you can see, we use the entityManager for read and write operations in the same transaction that can execute a rollback automatically if any of these operations fails.

For more details check the repository of TypeORM with examples here.

So if you want to wrap both actions in the same transaction, you need to call a method and pass the repository as a parameter. e.g:

async findOneItem(
  itemId: number,
  myRepository: MyRepository = this.myRepository, // Optional parameter if needed
) {
  return await myRepository.findOne(itemId);
}
over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda