How to Run a Node.js App Locally: Built-in Watch Mode for Efficient Development

Use Node.js's built-in --watch flag to automatically restart your application on file changes without installing external dependencies like nodemon.

When you start Node.js project development locally, having a fast feedback loop is essential for productivity. According to the Node.js source code, the runtime now includes a native watch mode that supervises your process and handles automatic restarts, eliminating the need for external tools while integrating deeply with the core module system.

How Node.js Watch Mode Works

The built-in watch implementation lives entirely within the core codebase and operates with zero runtime overhead when disabled. Here is how the system processes your --watch flag:

CLI Option Parsing

When you append --watch to your command, Node.js first parses this through lib/internal/options.js. The options module exposes flags including --watch, --watch-path, --watch-preserve-output, and --watch-kill-signal via the getOptionValue function.

The Watch Mode Supervisor

If --watch is present, control passes to lib/internal/main/watch_mode.js instead of the standard entry point. This module serves as a supervisor that prepares a clean execution environment and removes watch-related flags from process.execArgv before building the command array for the child process.

Process Spawning and Monitoring

In the start() function (lines 94-100 of watch_mode.js), the original Node process spawns your application (node app.js) as a child using child_process.spawn. The child inherits the parent's environment plus WATCH_REPORT_DEPENDENCIES=1, enabling dependency tracking.

File System Watching

The supervisor instantiates FilesWatcher from internal/watch_mode/files_watcher.js to monitor paths specified by --watch-path. If no path is provided, the watcher operates in filter mode using the kShouldFilterModules flag, watching only modules that your application actually imports. When a file changes, the watcher emits a changed event.

Graceful Restart Logic

Upon detecting changes, the restart() function (lines 61-73) sends the signal defined by --watch-kill-signal (defaulting to SIGTERM) to the child process. The supervisor waits up to 500ms for a graceful exit via reportGracefulTermination(), then re-spawns the child. Unless you specify --watch-preserve-output, the console clears between restarts.

Setting Up Your Local Development Workflow

Basic Watch Mode Setup

Add a development script to your package.json that points to your entry file:

{
  "scripts": {
    "dev": "node --watch app.js"
  }
}

Run npm run dev to start the supervisor. The process will restart automatically whenever any imported module changes.

Advanced Configuration Options

Watch specific directories to avoid spurious restarts from unrelated files:

{
  "scripts": {
    "dev": "node --watch --watch-path=src app.js"
  }
}

Preserve console output across restarts when debugging initialization logic:

{
  "scripts": {
    "dev": "node --watch --watch-preserve-output app.js"
  }
}

Customize the termination signal if your application requires specific shutdown handling:

{
  "scripts": {
    "dev": "node --watch --watch-kill-signal=SIGINT app.js"
  }
}

Debugging with Watch Mode

Combine watch mode with the inspector to attach a debugger on each restart:

{
  "scripts": {
    "debug": "node --inspect --watch app.js"
  }
}

Practical Code Examples

Run these commands directly in your terminal for different development scenarios:


# Basic watch - restarts on any module dependency change

node --watch app.js

# Watch only the src directory

node --watch --watch-path=src app.js

# Keep console history visible between restarts

node --watch --watch-preserve-output app.js

# Debug mode with custom kill signal

node --inspect --watch --watch-kill-signal=SIGINT app.js

Complete package.json configuration example:

{
  "name": "my-app",
  "main": "app.js",
  "scripts": {
    "dev": "node --watch --watch-path=src --watch-preserve-output app.js",
    "debug": "node --inspect --watch app.js"
  }
}

Summary

  • Native watch mode in lib/internal/main/watch_mode.js provides dependency-free file watching and process management.
  • Zero overhead when disabled, with deep integration into Node's module loader and signal handling.
  • Granular control via --watch-path, --watch-preserve-output, and --watch-kill-signal flags.
  • Graceful restarts wait 500ms for cleanup before respawning the child process.
  • Debugger compatibility allows --inspect to work seamlessly with automatic restarts.

Frequently Asked Questions

What is the difference between --watch and nodemon?

Node.js's built-in --watch requires zero dependencies and integrates directly with the core runtime's module system, whereas nodemon is an external package that adds abstraction layers. The native implementation in lib/internal/main/watch_mode.js specifically handles WATCH_REPORT_DEPENDENCIES and uses the internal FilesWatcher class, providing more predictable behavior aligned with your Node.js version.

How do I prevent restarts when unrelated files change?

Use the --watch-path flag to specify exact directories, or omit it entirely to enable the kShouldFilterModules filter mode. In filter mode, the watcher only monitors files that are actually imported by your application, ignoring unrelated changes in the filesystem.

Can I use watch mode in production?

While technically possible, watch mode is designed for local development. The process spawning logic in lib/internal/main/watch_mode.js creates a supervisor-child relationship that adds complexity unnecessary for production deployments. Use process managers like systemd, PM2, or container orchestration for production reliability.

Why does my app take 500ms to restart?

The restart() function in lib/internal/main/watch_mode.js implements a 500ms grace period to allow reportGracefulTermination() to complete cleanup operations. If your process exits immediately upon receiving SIGTERM, the supervisor proceeds immediately; otherwise, it waits for the timeout before forcefully respawning the child.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →