Editor’s note: This article was last updated by Yan Sun on 27 February 2024 to cover integrating environment variables with nodemon, as well as advanced nodemon configuration options like using the --ignore
option to ignore functions or monitoring multiple directories with the --watch
option.
nodemon is a CLI for Node.js that speeds up JavaScript development by restarting an execution process when a file is updated. For example, if we have a project with an index.js
file that we want to test and iterate on quickly, we can run nodemon index.js
. A new Node.js execution process will begin for index.js,
restarting whenever a file in the project is updated. Simple, right?
However, when TypeScript is introduced into the project and our project becomes more complex, the simplicity offered by nodemon diminshes. But don’t worry! In this article, we’ll explore three ways of setting up nodemon, each with different features and functionalities that can meet the needs of our TypeScript project.
If you are looking for alternatives to nodemon that will better fit the project’s requirements, we will also review three nodemon alternatives with extra features and more customizability. As each option has pros and cons, we will assess their suitability for our project to identify the best 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 a 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 installing a version of nodemon ≥1.19.0. In addition, ts-node
must be installed in our project. Because both packages will only be used during development, they should be installed as devDependencies
. devDependencies
are dependencies that are used during development, such as build tools, but are not necessary for the application’s runtime:
yarn add --dev nodemon ts-node # or use npm npm install --save-dev nodemon ts-node
Once both dependencies are installed, we can pass a TypeScript file to nodemon as the following command, which will monitor changes in the main.ts
file and restart the application accordingly:
npx nodemon ./main.ts
This method is a simple approach, requiring 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 the default tsc
TypeScript compiler used by ts-node
, and others may require advanced configurations. If this resonates with our project requirements, consider proceeding to method two.
The inbuilt nodemon TypeScript runner allows us to customize the behavior of the development environment to specific project needs using configuration files.
If the project requires more flexibility in how the files are executed, we can create a nodemon configuration file to meet the project’s exact specifications. By using a custom configuration file, we 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, with 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
:
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:
{ "execMap": {} }
Inside the execMap
object, create a new property for ts
files. The value of this property should be the command to run when executing the TypeScript files. For example, we can set it to ts-node
, or any other execution script or command:
{ "execMap": { "ts": "ts-node" } }
Voilà, nodemon is now configured to run a custom command for TypeScript files. When we 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
).
To convey the file path within the custom command in execMap
, use {{pwd}}
where the file path needs placement. For example, if the execMap
command for .js
files is node {{pwd}} && echo "Hello world"
, then calling nodemon index.js
will result in running node index.js && echo "Hello world"
.
Using a custom nodemon configuration file opens up a lot of flexibility that many projects require. We can configure many settings, as explained by the nodemon documentation.
To that extent, this method should only be used in cases where the first method does not meet the project’s requirements. If our project only needs TypeScript files to be compiled and run, then the inbuilt nodemon TypeScript support with ts-node
(method one) will likely be the best option for the project.
If our 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 an outside tool to bootstrap or execute our project.
While methods one and two offer ways to execute a single file, this method provides a way to execute a single command, thereby offering the most flexibility.
In the package.json
file, create a start
script. This command will be run and restarted by nodemon when a file changes.
To execute this command with nodemon, run:
nodemon --exec "yarn start" # or nodemon --exec "npm run start"
This passes the start
script as the executable command for the project by nodemon.
We can make the full nodemon command (i.e., nodemon --exec "yarn start"
) a dev
script. Then, we can call yarn dev
to run the nodemon custom command.
Although this method offers the most flexibility in terms of what can be run, it negates 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 suit the project’s needs.
Environment variables are dynamic configurations for our application in different environments, such as development or production. dotenv is a popular package that allows us to manage environment variables in a separate file, enhancing organization.
When we use nodemon together with dotenv, it will streamline our development by managing configurations and allow continuous testing without manual restarts.
First, let’s install the dotenv
package:
npm install --save-dev dotenv #or use yarn yarn add --dev dotenv
Then, we must create a .env
file at the root of our project directory:
PORT=3000 NODE_ENV=development
We can use the --require(-r)
option to preload the dotenv
config file. To demonstrate the use of the dotenv
config file, let’s add the line below to the index.ts
:
console.log(`environment: ${process.env.NODE_ENV}, port:${process.env.PORT}`);
Run the following command to display the values of the environment variables in the console:
nodemon -r dotenv/config index.ts
Alternatively, we can add the -r
option to the nodemon.json
config file as below to skip the -r
option when running the nodemon command:
{ "execMap": { "ts": "ts-node -r dotenv/config" } }
Now, when we run nodemon, it will pick up changes in TypeScript files and reload your application with the specified environment variables.
nodemon offers advanced configuration options, allowing us to customize behavior to specific project requirements. Here are some of the common options:
--ignore
)--ignore
option. This is handy when certain files or folders should not trigger a restart. For example, nodemon will ignore the lib/app.js
file when running the following command:
nodemon --ignore lib/app.js
--watch
)--watch
option to include additional directories:
nodemon --watch app --watch libs index.ts
With the above command, nodemon will monitor the “app
” and “libs
” directories for changes.
**--ext**
).js
, .mjs
, .coffee
, .litcoffee
, and .json
extensions. To define a custom list of file extensions, we can use the --ext
option:
nodemon -e ts,js,conf
In this example, a change of files with extensions of ts
, js
, and conf
will trigger nodemon to restart.
--delay
)---delay
option.For example, the following command runs nodemon with a delay of 10 seconds before restarting the application:
nodemon --delay 10 index.ts
nodemon.json
config file. In the following example, we define a restart
event to clear the console screen upon nodemon restarts:
"events": { "restart": "clear" }
Below is the list of the nodemon events:
start
: The child process has startedcrash
: The child process has crashed (nodemon will not emit exit)exit
: The child process has cleanly exited (i.e., no crash)restart
: The child process has restartedconfig:update
: nodemon’s config has changedLet’s look at some common issues of using nodemon, and how to address them.
"Command Not Found: nodemon"
If we encounter the "command not found"
error, it means nodemon isn’t installed correctly. To fix the issue, we can either install nodemon globally or locally as the project’s dev dependencies.
To install nodemon globally, follow the command below:
# install nodemon globally npm install -g nodemon # yarn yarn global add nodemon
To install nodemon as a project dev dependency, use the following command:
# install nodemon as a development dependency npm install --save-dev nodemon
Then, add the following script command into the package.json
file. We should be able to run nodemon by running npm run start
:
"start": "nodemon index.ts"
ts-node
and nodemonWhen using ts-node
and nodemon together, we may experience a slow startup time due to TypeScript compilation. As ts-node
transpiles TypeScript code to JavaScript during runtime, the initial compilation can be time-consuming, resulting in slower startup times, especially in larger projects or handling complex TypeScript files
Here are some tips to improve the performance.
--transpile-only
flagThe --transpile-only
flag skips type checking during compilation, which makes it much faster:
ts-node --transpileOnly # or in nodemon.json { "execMap": { "ts": "ts-node --transpileOnly" } }
swc
swc
(Speedy Web Compiler) is a TypeScript-compatible transpiler that is significantly faster. To use swc
, we need to install it first:
npm install --save-dev @swc/core
Then add the following into tsconfig.json
to enable swc
:
{ "ts-node": { "swc": true } }
nodemon is certainly a powerful tool for rapid development with Node.js. However, numerous alternatives may be better suited for our 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.
ts-node-dev
In the first method, we discussed how nodemon uses ts-node
to compile and run TypeScript files. ts-node-dev combines the file-watching capabilities of nodemon with the TypeScript support from ts-node
into a nodemon-like service tailored explicitly 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
:
yarn add --dev ts-node-dev # or use npm npm install --save-dev ts-node-dev
Then, to run the file and restart on file changes, run:
ts-node-dev --respawn index.ts # or tsnd --respawn index.ts
Replace index.ts
with the entry file to the 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 our project.
PM2 is a battle-tested and production-ready process manager for Node.js programs with numerous features and configuration options. It manages 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 these features, PM2 offers an auto-restart functionality that restarts the program when a file is changed.
To get started with PM2, install it globally on our system:
npm install pm2 -g
Next, we will have to configure a little bit. Create a file named ecosystem.config.json
, and enter the following contents:
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
to run ts-node index.ts
. Finally, run:
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 the application should be printed on the terminal, and a column titled Watching should read Enabled for the application. This application will run in the background until we 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 be overkill for our project.
If we are just looking for a simple way to restart TypeScript projects, this method will likely not be the best choice for the project, and we should consider other alternatives or nodemon methods.
Sometimes, the best way to do something is to do it entirely from scratch.
As we have seen in all of the previous methods and alternatives, there is always a potential negative or drawback to using one option instead of another. We can avoid these limitations by creating a file watcher from scratch and learning 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:
// 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 we can write the entire file watcher in TypeScript! To run the file watcher, run it with ts-node
:
ts-node runner.ts
This method, by far, offers the most customizability, as we are creating the entire file watching process. We can spawn a different child process if needed or spawn multiple, and we can run any other JavaScript/TypeScript code as needed when a file is updated.
However, unlike the other options in this article, this DIY solution requires maintaining the runner. If you’re confident in your abilities, this alternative option should certainly not be overlooked!
There are numerous ways in which nodemon can be configured to fit the project’s needs and requirements. However, if none of those nodemon methods works, there are also ample alternatives that may offer different advantages over nodemon for your project. I hope you found a method in this article that will suit your specific use case.
LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowSOLID principles help us keep code flexible. In this article, we’ll examine all of those principles and their implementation using JavaScript.
JavaScript’s Date API has many limitations. Explore alternative libraries like Moment.js, date-fns, and the new Temporal API.
Explore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.
3 Replies to "Configuring nodemon with TypeScript"
There is an option while using Typescript client:
tsc –watch
Or shortcut:
tsc -w
Hey Baptiste, thanks for your comment! The “tsc” command will only compile your typescript code and stops short of actually running or executing it. But if that’s all your project needs, it’s certainly a viable option!
This saved my life, thanks!
You have a typo in the pm2 sectgion, though: “Create a file named ecosystem.config.json”
This should have been config.js instead.
But thanks for putting everything together