NestJS Dependency Injection in Worker Threads
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.
You shouldn't use worker threads for I/O operations as node.js already gracefully handles this for you.
If you're eager to dive into the complete example right away, you can check out the GitHub repo for the full example.
In order to illustrate this, we will use a very simple service that calculates the fibonacci sum, which is a CPU-intensive task, a great candidate for node.js worker threads!
Now, let's dive into the code snippet that launches the worker thread:
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.
We are using the constant workerThreadFilePath
from associated config file on the same level in the directory tree so we can safely use the path for the worker thread regardless of where the app is deployed.
Take a note that we are using the js extension here because the transpiled output of typescript, as we know, it's javascript.
Let's explore the file which will be executed in the worker thread:
In order to have access to dependency injection in our NestJs app in the thread, we leverage Nest standalone application which is a wrapper around the Nest IoC container, which holds all instantiated classes.
Now, we can execute any method from any service.
Caveats:
- Be cautious when using dependency injection from NestJS in a worker thread, as it comes with a cost. The startup process of the NestJS app must be awaited before accessing the dependency injection. Therefore, utilize this method only if absolutely necessary, or if the startup bottleneck is negligible in terms of overall performance.
- 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 repository:
If you'd like me to cover more interesting topics about the node.js ecosystem, feel free to leave your suggestions in the comments section. Don't forget to subscribe to my newsletter on rabbitbyte.club for updates!