Search code examples
foreachnestjstypeormasync.js

Async function not allowed in forEach with Typeorm query


I'm trying to do a Typeorm query depending on an element of an object but I have an issue with the forEach and the async function.

I have read a lot of similar issues but no one worked for me as expected.

Here is my code snippet with the await not allowed in a forEach:

public async runBudgetLimit(): Promise<void> {
        const contracts: ContractEntity[] = await this.contractService.getAllProjects();

        contracts.forEach((contract) => {
            const project: UserEntity = await this.userService.findOne(contract.milestoneId);
            const date: string = Time.moment().format('YYYY-MM-DD');
            const budgetUsed = this.trackService.getBillingByProject(project.contractId, date, '1000', '1000', true);
        });
    }

Here is the async Typeorm query:

async findOne(id: string): Promise<UserEntity | undefined> {
        return this.userRepository.findOne(id);
}

I don't know what is the best solution to solve this issue, I don't think that the for loop is a good solution but I'm open to all solutions.


Solution

  • You can just use for-of loop like this:

    for (const contract of contracts) {
                const project: UserEntity = await this.userService.findOne(contract.milestoneId);
                const date: string = Time.moment().format('YYYY-MM-DD');
                const budgetUsed = this.trackService.getBillingByProject(project.contractId, date, '1000', '1000', true);
            }
    

    Or with slight optimisations - check this answer for differences between for-of and Promise.all approaches:

    await Promise.all(contracts.map(async contract => {
                const project: UserEntity = await this.userService.findOne(contract.milestoneId);
                const date: string = Time.moment().format('YYYY-MM-DD');
                const budgetUsed = this.trackService.getBillingByProject(project.contractId, date, '1000', '1000', true);
            }));
    

    Keep in mind that these solutions are not optimal in some cases, because by getting UserEntity for every contract there is an additional query to the database AKA N+1 query problem. To fix this you can load all of the users along with the contracts by using the relations array.

    I am not sure why are you getting contractId from the project, isn't it available on the contract object?

    Also if the response from getBillingByProject is a promise you should put await in front