— January 30, 2022
Read about three different ways you can configure nodemon with TypeScript — and explore three alternatives to nodemon for other use cases.
nodemon is a CLI for Node.js that makes JavaScript development much quicker by restarting an execution process when a file is updated. For example, if you have a project with an index.js
file that you want to quickly test and iterate on, you can run nodemon index.js
, and a new Node.js execution process will begin for index.js
, restarting whenever a file in the project is updated. Simple, right?
Well, the simplicity offered by nodemon diminishes both as you introduce TypeScript into your project, and as the complexity of your project grows. But fear not! In this article, we’ll review three methods of configuring nodemon, each of which offers different features and functionalities that can meet the needs of your TypeScript project.
We will also review three nodemon alternatives with extra features and more customizability, if you are looking for alternatives to nodemon that will better fit your project’s requirements. Since each option has its own pros and cons, we will discuss whether or not each option will suit the needs of our project, and if not, which option is a better choice.
As of v1.19.0, nodemon has inbuilt support for TypeScript files with help from ts-node
that requires no manual configuration. By default, nodemon uses the node
CLI as an execution program for running JavaScript files; for TypeScript files, nodemon uses ts-node
as the execution program instead.
ts-node
is a TypeScript execution engine that compiles and runs TypeScript files. ts-node
serves as drop-in replacement for the node
CLI, so the same arguments can be passed to the ts-node
CLI as the node
CLI.
This method requires a version of nodemon ≥1.19.0 to be installed. In addition, ts-node
must be installed in your project. Since both of these packages will likely only be used during development, they should be installed as devDependencies
.
1
yarn add --dev nodemon ts-node
Once both of these dependencies are installed, you can pass a TypeScript file to nodemon as you would a JavaScript file.
1
npx nodemon ./main.ts
This method is by far the most simple, as it requires minimal setup. It’s built into nodemon itself, so all that is required is installing the necessary packages.
However, this method falls short in terms of flexibility and customization. Many projects require more than just the default tsc
TypeScript compiler that is used by ts-node
, and still others will require more advanced configuration; if this scenario describes your needs, proceed to method two.
The inbuilt nodemon TypeScript runner provides a method to get up and running with minimal setup: manual configuration.
If your project requires more flexibility in how your files are executed, nodemon allows users to create a configuration file to meet a project’s exact specifications. By using a custom configuration file, you can reap the maximum benefits of nodemon’s flexibility and take advantage of all of its offered settings.
The specific setting we will be configuring is execMap
, or execution map. This setting informs nodemon about which executables or commands to run for different file types. For now, we’ll go over how to set up an execution map specifically for TypeScript files.
To create a configuration file, make a new file in your project’s root directory named nodemon.json
.
1
touch ./nodemon.json
In the nodemon.json
file, create a new JSON object with an execMap
property. The value of the execMap
property should be an object.
1
2
3
{
"execMap": {}
}
Inside the execMap
object, create a new property for ts
files. The value of this property should be whatever command you want to run when executing your TypeScript files. For example, you can set it to ts-node
, or any other execution script or command.
1
2
3
4
5
{
"execMap": {
"ts": "ts-node"
}
}
Voilà, nodemon is now configured to run a custom command for TypeScript files. When you call nodemon with a TypeScript file (i.e., nodemon index.ts
), nodemon will find the command in the execMap
that correlates to .ts
files and then run that command, passing the file as the final argument (i.e. ts-node index.ts
).
If you want to pass the filepath elsewhere in the command (i.e., not as the final argument), type {{pwd}}
where the filepath should be placed in the command. For example, if your execMap
command for .js
files is node {{pwd}} && echo "Hello world"
, then calling nodemon index.js
will run node index.js && echo "Hello world"
.
Using a custom nodemon configuration file opens up a lot of flexibility that many projects require. There are a lot of settings that you can configure, as explained by the nodemon documentation.
To that extent, this method should only be used in cases where the first method will not meet your project’s requirements. If your project only needs your TypeScript files to be compiled and ran, then the inbuilt nodemon TypeScript support with ts-node
(method one) will likely be the best option for your project.
If your project happens to need even more customization, consider method three.
nodemon shines as a tool to help run and restart the execution of a single file when any file in a project is updated. However, not all projects have a single entry point; that is, many modern projects require the use of an outside tool to bootstrap or execute your project.
Whereas methods one and two offer ways to execute a single file, this method will offer a way to execute a single command, thereby offering the most flexibility of these methods.
In your package.json
file, create a start
script. This will serve as the command that will be run and restarted by nodemon when a file changes.
To execute this command with nodemon, run:
1
2
3
nodemon --exec "yarn start"
# or
nodemon --exec "npm run start"
This passes the start
script as the executable command to run for your project by nodemon.
You can make the full nodemon command (i.e., nodemon --exec "yarn start"
) a dev
script, such that calling yarn dev
will run nodemon with your custom execuction command.
Although this method offers the most flexibility in terms of what can be run, it does negate nodemon’s most notable feature: (re)running the execution of a single file when a file in the project is updated.
Before choosing this method, consider whether methods one or two are better suited for your project’s needs.
nodemon is certainly a powerful tool for rapid development with Node.js. However, there are also numerous alternatives that may be better suited for your project.
In the next part of this post, we’ll consider three alternatives to nodemon: ts-node-dev
, pm2
, and a DIY file watcher built with Parcel.
In the first method, we discussed how nodemon uses ts-node
to compile and run TypeScript files. [ts-node-dev](
https://github.com/wclr/ts-node-dev
)
combines the file-watching capabilities of nodemon with the TypeScript support from ts-node
into a nodemon-like service that is specifically tailored to TypeScript.
ts-node-dev
interfaces directly with the TypeScript execution engine and compilation process to offer a more efficient system than nodemon for TypeScript files. ts-node-dev
only reloads when changes are made to files that are a dependency of (i.e., imported by) the entry file. Additionally, ts-node-dev
shares a singular compilation process between restarts to maximize efficiency and make restarts quicker.
To use ts-node-dev
, first install it as a devDependency
:
1
yarn add --dev ts-node-dev
Then, to run your file and restart on file changes, run:
1
2
3
ts-node-dev --respawn index.ts
# or
tsnd --respawn index.ts
Replace index.ts
with the entry file to your project.
ts-node-dev
is a great option for fast TypeScript development because it is more efficient than nodemon, and is made specifically for TypeScript.
However, while it does offer some level of configuration, ts-node-dev
is arguably much less customizable than nodemon. It also does not restart on changes to static assets, which can be useful when serving images on a web server. Make sure to consider these downsides before choosing ts-node-dev
for your project.
pm2 is a battle-tested and production-ready process manager for Node.js programs that is loaded with numerous features and configuration options. It is used to manage multiple Node.js applications and processes, and comes with a load balancer to manage heavy applications with high amounts of queries.
pm2
supports hot reloading, application monitoring, and detailed process management. In addition to all of these features, pm2
offers an auto-restart functionality that will restart your program when a file is changed.
To get started with pm2
, install it globally on your system.
1
npm install pm2 -g
Next, we will have to do a little bit of configuration. Create a file named ecosystem.config.json
, and enter the following contents:
1
2
3
4
5
6
7
8
9
module.exports = {
apps: [
{
name: "TSServer",
script: "ts-node",
args: "index.ts", // replace this with your project's entry file
}
]
}
This will create a new app called “TSServer” that will run ts-node index.ts
. Finally, run:
1
pm2 start ecosystem.config.js --only TSServer --watch
This will run the TSServer app and restart on file changes with the watch
argument. A fancy table with information about your application should print to the terminal, and a column titled Watching should read Enabled for your application. This application will now run in the background until you call pm2 stop TSServer
.
As previously stated, pm2
is jam-packed with exciting features that are incredibly useful for large production applications. However, for this reason, pm2
may very well be overkill for your project.
If you are just looking for a simple way to restart TypeScript projects, this method will likely not be the best choice for your project and you should consider other alternatives or nodemon methods.
Sometimes, the best way to do something is it do it entirely by yourself from scratch.
As we have seen in all of the previous methods and alternative, there is always a potential negative or drawback to using one option instead of another. You can avoid these limitations by creating a file watcher from scratch, and even learn something along the way!
For this DIY file watcher, we will take advantage of the capabilities provided by the Parcel file bundler, which can be utilized for developing web apps or Node.js libraries.
Parcel exposes a JavaScript API to watch events in the bundling process. Each time a file is updated, the bundling process for our TypeScript project restarts. When the bundling process completes, we will spawn a child process that executes the bundled and compiled JavaScript file.Here is an example of my DIY file watcher built with Parcel:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// make sure you have @parcel/core and @parcel/config-default
// installed as devDependencies
import {Parcel} from '@parcel/core';
import {spawn, ChildProcessWithoutNullStreams} from 'child_process';
let bundler = new Parcel({
entries: 'src/index.ts',
defaultConfig: '@parcel/config-default',
defaultTargetOptions: { distDir: `${process.cwd()}/dist` },
});
async function main() {
let cp: ChildProcessWithoutNullStreams;
await bundler.watch(() => {
cp?.kill()
cp = spawn("node",[`${process.cwd()}/dist/index.js`])
cp.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
})
cp.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
});
}
main()
Another benefit of this method is that you can actually write your entire file watcher in TypeScript! To run your file watcher, simply run your file with ts-node
.
1
ts-node runner.ts
This method, by far, offers the most customizability, since you are creating the file watching process yourself. You can spawn a different child process if needed, or spawn multiple, and you can run any other JavaScript/TypeScript code as needed when a file is updated.
However, as this is a DIY solution, it is your own responsibility to upkeep and maintain the runner, whereas this is done for you by teams of knowledgeable open source developers for all of the other options provided in this article. As long as you know what you are doing, however, this alternative option should certainly not be overlooked!
There are numerous ways in which nodemon can be configured to fit your project’s needs and requirements. However, if none of those methods work for you, there are also ample alternatives that may offer different advantages over nodemon for your project. I hope you have found a method in this article that will suit your specific use case.
Don’t miss out on the latest content! Join over 3k+ devs in subscribing to Instructive.dev.
Join the mailing list