Keeping tabs on file changes during development is an essential part of the workflow for most Node.js developers. It’s all about detecting changes in files to set off actions like server reloads, code recompiles, or automated testing.
With native file watching now in place in Node.js, you no longer have to worry about manually updating your development environment whenever you make changes in the codebase.
In this article, we will look at the file watching feature built into Node.js and how it measures up against Nodemon. We will also walk you through migrating from Nodemon to native file watching and highlight other updates with this Node.js release.
Nodemon is a widely used utility in the Node.js ecosystem that’s helped enhance developer productivity to quite an extent. It ensured that developers no longer had to stop and start their server again and again after every change made during development.
Nodemon’s simplicity and usability made it a vital tool for any Node.js developer looking to have a stress-free development process. Before Nodemon was introduced, manual restarts were time-consuming and stressful.
Nodemon’s ability to automate this task was a game-changer, providing:
Nodemon really improved the development workflow by automating restarts, which was very important before the introduction of native file watching. But with a stable native file watching feature in the latest Node release, we may no longer need to rely on third-party solutions like Nodemon or Turbowatch.
The native file watching implementation aims to bring a new dynamic into the Node development workflow for so many developers out there. Does that mean Nodemon is no longer necessary? Let’s compare native file watching with Nodemon based on features and usability in general.
Native file watching in Node.js offers important features, like:
--watch
and --watch path
--watch-preserve-output
to control console behavior during restartsHowever, Nodemon still offers many benefits, such as:
nodemon.json
or package.json
. Developers can specify files or directories to watch, files to ignore, and delay before restartexecMap
property in the nodemon.json
config filers
command during developmentHere’s a summary of the key differences between Nodemon and native file watching:
Feature | Native file watching | Nodemon |
---|---|---|
Installation | Built-in with Node.js (requires version 18.11.0 or higher) | Requires npm or yarn installation (npm install -g nodemon) |
Usage | node –watch app.js | nodemon app.js |
Configuration | Basic path watching options | Extensive configuration via nodemon.json or package.json |
Extensions supported | Limited to JavaScript by default | Extensive, can monitor various file types |
Manual restart | Not available | Supports manual restart by entering the rs command |
Custom executables | Not available | Supports custom executables via execMap |
Event triggering | Not available | Supports event hooks for restart, start, quit, etc. |
So, while stable native file watching support in Node.js is undoubtedly a huge win, there are still some use cases where you might need the more advanced features offered by Nodemon.
The choice between native file watching and Nodemon largely depends on your use case and development needs:
Consider the strengths and limitations of each tool to make an informed decision and choose the option that best suits your project requirements and workflow preferences.
To use native file watching in your development projects, you must have Node.js installed and a version compatible with it. Open your terminal and enter the command below to see what version of Node is installed on your machine:
node -v
If the output from the command you entered in your terminal was at least version 18.11.0, you’re good to go! If not, you’ll need to update the Node.js runtime installed on your machine to a more recent version from the Node.js website.
Next, go to any empty folder of your choice and create a new file called index.js
with the following code:
console.log("Native File Watching Demo");
To start using native file watching, use the --watch
flag when starting your Node.js application. This approach simplifies the setup, as you no longer need an external tool to keep track of changes:
node --watch index.js
By running the above command, Node.js will watch the index.js
file and all required or imported modules and automatically rerun the process when any watched files change:
If you want to watch specific directories, you can do so by using the --watch-path
flag. It allows you to limit file watching to certain paths, which can be useful for large projects with numerous files. You can reduce unnecessary resource usage by focusing only on the directories that matter:
node --watch-path=./src --watch-path=./tests index.js
This ensures that only the src
and tests
directories are monitored.
It’s also worth noting that when specifying the paths to watch, the default behavior when using the --watch
flag to watch for imported or required modules is disabled.
Migrating an existing project from Nodemon to native file watching involves updating your setup to leverage Node.js’s built-in capabilities. Here’s how you can smoothly make the switch.
Start by updating your package.json
start scripts to use the native file-watching flag instead of invoking Nodemon. This change reduces the dependencies in your project and leverages Node.js’s built-in capabilities:
{ "scripts": { "dev": "node --watch index.js" } }
Use the --watch-path
flag if your project structure requires watching specific directories. For example, to watch both the src
and tests
, modify the dev
script as follows:
{ "scripts": { "dev": "node --watch-path=./src --watch-path=./tests index.js" } }
By default, the console output is cleared when there are restarts in watch mode. This behavior can be useful to avoid clutter, but it might also remove important debugging information. Use the --watch-preserve-output
flag to preserve the console output for easier debugging:
{ "scripts": { "dev": "node --watch --watch-preserve-output index.js" } }
After these changes, use the appropriate npm or Yarn command to start your application in development mode as usual.
Node.js v22 introduced several updates, improving various aspects of the runtime. Among these updates are:
require()
to load synchronous ES modules under the --experimental-require-module
flagpackage.json
using node --run <script-in-package-json>
glob
and globSync
methods to the fs
module for file path pattern matchingLet’s take a quick look at each of these updates and why they make this Node release so exciting.
In Node.js v21, a browser-compatible WebSocket client was introduced as an experimental feature, requiring the --experimental-websocket
flag to enable it:
node --experimental-websocket index.js
With Node.js 22, the WebSocket client is now stable and enabled by default, eliminating the need for the experimental flag.
Here’s how to set up a simple WebSocket server connection with the WebSocket
global:
const socket = new WebSocket("ws://localhost:8000"); socket.addEventListener("open", (event) => { socket.send("Hi!"); }); socket.addEventListener("message", (event) => { console.log("Message: ", event.data); });
This update provides a WebSocket client to Node.js without external dependencies, simplifying real-time communication in your applications.
require()
Node.js 22 introduces support for requiring synchronous ES modules using the require()
function under the experimental flag --experimental-require-module
to make projects using the older CommonJS module system compatible with ES modules.
To use this feature, enable the experimental flag:
node --experimental-require-module index.js
Some of the requirements for using this feature are:
"type": "module"
field in the nearest package.json
or have a .mjs
extensionawait
)Here is how to import an ES module in a CommonJS module. First, create your ES module:
// module.mjs export function greet() { return "Hello from ESM!"; }
Require the ES module synchronously in your index.js
:
// index.js const esmModule = require("./module.mjs"); console.log(esmModule.greet());
Using the require()
function, the specified ESM module is loaded synchronously, and its namespace object is returned. This is similar to the dynamic import()
, but without the asynchronous behavior.
When you run the code, you should see the following output in your terminal:
Hello from ESM! (node:40861) ExperimentalWarning: Support for loading ES module in require() is an experimental feature and might change at any time (Use `node --trace-warnings ...` to show where the warning was created)
The warning message you’ll see in your terminal is because the feature is still experimental.
This enhancement aims to provide a smoother integration between CommonJS and ES modules, which is expected to become stable in future Node.js releases.
package.json
scriptsA new experimental feature in this Node.js release lets you execute scripts defined in package.json
using the node --run
command. This offers a faster alternative to running scripts than npm run <script>
.
To use this feature, define scripts in your package.json
:
{ "scripts": { "test": "node test.js" } }
Run the scripts using the node --run
command:
node --run test
The node --run
feature focuses on top performance for common use cases. It doesn’t aim to match the behaviors of npm run
or other package managers. Some intentionally excluded features are:
With this new feature, the development workflow gets faster by executing scripts directly from the command line using Node.js.
glob
and globSync
Two new functions, glob
and globSync
, were added to the fs
and fs/promises
modules to make it easier for developers to match file paths based on a specified glob pattern.
Here’s how you can use it asynchronously:
// Using glob (asynchronous) const { glob } = require('node:fs'); // Pattern to match all JavaScript files in the current directory and subdirectories const pattern = "**/*.js"; glob(pattern, (err, matches) => { if (err) throw err; console.log(matches); });
Here’s how you can use it synchronously:
// Using globSync (synchronous) const { globSync } = require('node:fs'); // Pattern to match all JavaScript files in the current directory and subdirectories const pattern = "**/*.js"; const matches = globSync(pattern); console.log(matches);
Since this feature is still experimental, you might get the following warning message when you use it:
(node:31707) ExperimentalWarning: globSync is an experimental feature and might change at any time (Use `node --trace-warnings ...` to show where the warning was created)
For more information and to explore other relevant updates in Node.js 22, check out the docs and official release notes.
Native file watching being stable in Node.js 22 raises a question: is it a worthy candidate to replace the industry favorite Nodemon?
While Nodemon offers several useful functionalities out of the box, the native file watching feature is directly integrated into the runtime and provides ease of use that will make it an attractive option for many developers.
Now, it’s up to developers like you and me to try it and share our experiences to help shape the future of Node.js tooling. Happy coding!
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third-party services are successful, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.
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 nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.