Running Your First Script
The REPL is for quick experiments; real programs live in files. This page shows how to write a .js file, run it, pass arguments, choose the module system, and use the modern watch mode so you do not restart manually after every edit.
Create and run a file
Create app.js with any text editor:
app.js
const now = new Date().toLocaleTimeString()
console.log('Hello! It is', now)Run it from the same folder:
node app.js
Hello! It is 2:14:08 PM
What Node does when you run a file
Understanding the startup sequence demystifies a lot of "why did it do that?" moments:
From command to output
node app.js
1. Initialize V8 and the libuv event loop
2. Decide module system: .mjs → ESM, .cjs → CommonJS,
.js → look at "type" in the nearest package.json
3. Resolve, load, and execute app.js top to bottom
4. Keep the process alive while the event loop has work
5. Exit with code 0 when the loop drains (or the code you set)ESM vs CommonJS — which one runs?
File / setting | Module system | Syntax |
|---|---|---|
| ES Modules |
|
| CommonJS |
|
| ES Modules |
|
| CommonJS |
|
Reading command-line arguments
Arguments after the filename live in process.argv (an array of strings). The first two entries are the Node binary and the script path; your arguments start at index 2:
greet.js
const name = process.argv[2] || 'stranger'
console.log(`Hello, ${name}!`)node greet.js Alice
Hello, Alice!
We cover parsing, flags, and util.parseArgs in depth in Command-Line Arguments (process.argv).
Watch mode — auto-restart on save
Node 18.11+ has a built-in --watch flag that reruns your script whenever a dependent file changes — no nodemon needed. --watch-path limits what it watches:
node --watch app.js # rerun on any imported file change node --watch-path=./src app.js # only watch ./src
npm scripts — the conventional entry point
Rather than memorizing flags, teams put run commands in package.json so everyone uses the same invocation:
package.json
{
"scripts": {
"start": "node app.js",
"dev": "node --watch --env-file=.env app.js"
}
}npm run dev # runs the "dev" script above
Exit codes
When a script finishes successfully it exits with code 0; an uncaught error exits non-zero. CI pipelines and shell scripts branch on this, so set it deliberately:
if (somethingWentWrong) {
console.error('Failed!')
process.exitCode = 1 // preferred: let pending I/O drain, then exit 1
return
}Exit code
0= success.Non-zero = failure (CI tools treat this as a failed step).
You can chain in a shell:
node build.js && node deploy.jsruns deploy only on success.