Configuring nodemon with TypeScript
Tags: node, typescript, tutorial, javascript
Published: 2022-01-18 05:26:44

*Originally published on [the LogRocket blog](https://blog.logrocket.com/configuring-nodemon-with-typescript/).*

[nodemon](https://nodemon.io) 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.

## Method 1: No-config workflow

As of v1.19.0, [nodemon has built-in support for Typescript files](https://github.com/remy/nodemon/releases/tag/v1.19.0) 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`.

```bash
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.

```bash
npx nodemon ./main.ts
```

### Advantages and disadvantages

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.

## Method 2: Manual configuration

The built-in nodemon TypeScript runner provides a method to get up and running with minimal setup: [manual configuration](https://github.com/remy/nodemon#config-files). 

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`.

```bash
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.

```json
{
    "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.

```json
{
    "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`).

**Bonus tip:** 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"`.

### Advantages and disadvantages

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](https://github.com/remy/nodemon/tree/master#readme). 

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.

## Method 3: Custom execution command

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](https://github.com/remy/nodemon#running-non-node-scripts), 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:

```bash
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.

**Bonus tip:** 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.

### Advantages and disadvantages

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.

---

## What are some alternatives to nodemon?

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](https://blog.logrocket.com/what-you-need-to-know-about-parcel-2/).

## Alternative 1: ts-node-dev

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`:

```bash
 yarn add --dev ts-node-dev
```

Then, to run your file and restart on file changes, run:

```bash
ts-node-dev --respawn index.ts
# or
tsnd --respawn index.ts
```

Replace `index.ts` with the entry file to your project.

### Advantages and disadvantages
`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.

## Alternative 2: `pm2`

`[pm2](https://github.com/Unitech/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.

```bash
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:

```javascript
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:

```bash
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`.

### Advantages and disadvantages

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.

## Alternative 3: DIY file watcher with Parcel

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](https://gist.github.com/chroline/c84b14d76f2c6305ec388cb8bd5756ee):

```typescript
// 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`.

```bash
ts-node runner.ts
```

### Advantages and disadvantages

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!


## Conclusion

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.