Configuring PM2 in docker for Node.js

Solving path alias problem in both typescript and javascript

JiJay
5 min readJul 8, 2021

Node.js is javascript runtime environment that runs on V8 javascript engine.

As Node.js runs by event loop which is basically single threaded, it is important to manage multiple processes of node.js

Saying event loop of node.js is single threaded does not mean that node.js is entirely runs on single threaded. Actuall node.js uses multiple threads to do hard work behind the scens.

Although you can manage multiple processes by yourself in node.js using cluster mode, there is an awesome open source software called PM2 which helps you manage multiple processes of node.js.

It’s not hard to apply pm2 to your node.js project but when it comes to combine docker and typescript with pm2, it gets tricky.

I’m going to skip the details of node.js, pm2 and docker and focus on configuring pm2 and docker. Specifically, I will share my solution to path alias problems that bothered me a lot.

Path Alias for typescript

Path alias for typescript is not that difficult.

First, you have to configure baseUrl and paths in tsconfig.json.

tsconfig.json

This will make your Visual Studio Code recognize your path alias in typescript files.

When you run your codes with ts-node though, you need a library called tsconfig-paths to actually run your code with path alias.

First, install tsconfig-paths in devDependencies.

yarn add tsconfig-paths -Dor npm install tsconfig-paths --save-dev

Second, when you run your codes with ts-node, use -r option to apply tsconfig-paths.

ts-node -r tsconfig-paths/register src/index.ts

And it’s done!

It’s too easy right? But, it becomes difficult when it comes to javascript… 😢

Path Alias for javascript

When we run typescript files, we can run them by ts-node as we seen above but, ts-node is not adequate for production as it compiles multiple times , which leads to low speed though there are some options for that.

The common way to run typescript files is to first compile(or transpile) typescript files to javascript files using tsc and run those files using node, which is done by below commands.

tsc && node build/src/index.js

Directory for compiled files can be configured in tsconfig.json changing path for ‘outDir’

tsconfig.json

As I configured outDir to ./build, when I run tsc(npx tsc in command line), compiled javascript files will be contained in build directory.

When I run javascript files however, an error comes out saying ‘there is no such module @/src…’

Like we applied tsconfig-paths library in ts-node, we have to apply another library in node, which is module-alias.

First, install module-alias.

yarn add module-aliasor npm install module-alias

Second, we have to import module-alias/register in an entry file.

import moduleAlias from 'module-alias/register'

However, if you run this file with ts-node in development environment, it will overwrite the module path from tsconfig-paths. Therefore, it does not work in ts-node.

This was my main problem of applying path alias in pm2. There is an alternative which I do not use ts-node. Instead, I can compile typescript files and run those files in development environment.

If you adopt that solution, you can pass below. However, I decided to maintain ts-node in development environment so I will explain I solved this problem.

As It is impossible to apply moduleAlias just by writing static code, we have to apply moduleAlias depending on which environment we’re running the code. Therefore, when we run ts-node in development environment, ‘tsconfig-paths ‘ is applied and when we run node in production environment, ‘module-alias’ is applied.

index.ts

As mentioned above, only in production environment which can be discovered by environment variables I set before, I config moduleAlias using addAliases method.

However, as it should be applied before importing path alias, the code seems terrible(😹).(I turned off lots of eslint rules for this code..)

Upper part, I import modules that I need for apply module alias and then I apply moduleAlias only in production environment and finally I proceed my code with path alias.

If you think code readability is important, then you have to find out another solution.(Cheer up for those 👍)

That’s it for those who use only ts-node and plain node.js environment.(without docker or pm2). For those who use docker and pm2 for node.js, please cheer up! 👍

Path alias for pm2

There are many ways to use pm2 to run node.js as mentioned in pm2 documentation.

In my opinion, the best or not worst way to use a software or application is to use config file rather than command line.

Therefore, I will use ecosystem.config.js file for production, ecosystem.dev.config.js file for development to use pm2.

After writing ecosystem.config.js file, we can simply start pm2 by running.

pm2 start ecosystem.config.js

In ecosystem.config.js, we configure apps that we want to run through pm2.

ecosystem.config.js

We can configure each app. For instance, I use cluster mode for server app, using maximum available CPU cores. However, I run check table app using only one process.

As moduleAlias is applied by code in index.ts(index.js after compilation), there is no problem running production.

pm2 start ecosystem.config.js

If you use docker with pm2, you shoud use pm2-runtime to start pm2 in docker container.

pm2-runtime start ecosystem.config.js

However, in development, you should use pm2-dev.

pm2-dev start ecosystem.dev.config.js

In addition you need to edit ecosystem.dev.config.js to apply ts-node and tsconfig-paths when running node processes by pm2.

ecosystem.dev.config.js

We use ts-node and apply tsconfig-paths using -r option.

Conclusion

Though path alias is convenient especially if you have too much depths of files, it requires some effort when we use typescript and pm2.

There would be other better solutions so I recommend you to search other solutions if you have same problems with me.

I solved my problem by changing my entry file, which is dangerous as entry file is related to my service or business logic. It is recommended to contain codes in entry file only related to service or business logic. Also, the code seems terrible ruining some eslint-rules.

I do not like this way but this was the only solution I found. I’ll keep finding the better solution.

Thank you for reading. Bye 😁

--

--