NestJS Dependency Injection in Worker Threads

    I will show you how to use worker threads in a NestJS app.

    There may be cases when you need to do CPU-intensive tasks on the backend like big JSON parsing, video encoding, compression, etc. If you have at least 2 cores in your processor's configuration you can run javascript in parallel for these tasks and not block the main thread of the NestJS app that handles client requests.

    An excellent way of doing this is by using node.js worker threads.

Note: You shouldn't use worker threads for I/O operations as node.js already gracefully handles this for you.

    Let's dive into a worker thread code example:

import { Injectable } from '@nestjs/common';
import { Worker } from 'worker_threads';
import { workerThreadFilePath } from '../../workerThreads/config';

@Injectable()
export class WorkerThreadExample {
async runWorker() {
const worker = new Worker(workerThreadFilePath, {
workerData: 'data to be sent from main thread to worker',
});
worker.on('message', (message) => console.log('on message', message));
worker.on('error', (e) => console.log('on error', e));
worker.on('exit', (code) => console.log('on exit', code));
}
}

    As we can see from the snippet above, we defined a new worker, gave it a path to a file to be executed by the worker, and gave it as param the workerData. Worker data is data sent from the main thread to the other thread.

  In workerThreads/config.ts file, we are just defining the path for the file in a consistent way.


export const workerThreadFilePath = __dirname + '/worker.js';
    
    Worker constructor doesn't accept ts files and to overcome this you should add "allowJs": true to your tsconfig so NestJs can add  your js file to dist folder.

{
"compilerOptions": { // other options
"allowJs": true
}
}

    In order to have access to dependency injection of your NestJs app in the thread, we will import  NestFactory from @nestjs/core and  call the createApplicationContext promise giving our app module as parameter. 

import { NestFactory } from '@nestjs/core';
import { workerData } from 'worker_threads';
import { AppModule } from '../app.module';
import { ApiService } from '../modules/api/api.service';

async function run() {
const app = await NestFactory.createApplicationContext(AppModule);
const apiService = app.get(ApiService);
console.log(workerData); // data sent from main thread is available here
// business logic using nestjs dependency injection
}

run();
    

Caveats: 

    If your app setup is starting processes that you don't want to run on the separate thread you will need to configure your app module into a dynamic module to accept a parameter to not initiate the setup.

     Example:
const app = await NestFactory.createApplicationContext(
AppModule.register({ attachRabbitMqConsumers: false }),
);

Github: 


Comentarii