# Node.js process and CLI: Reading Arguments, Exit Codes, and stdio

Source: https://devdocs10.com/articles/javascript/js-node-process  
By Murat Bo · Published 2026-07-21 · DevDocs10 (https://devdocs10.com)

This article explains how Node.js gives your program access to the command line through `process`. You will learn how to read arguments, inspect environment variables, write output, handle errors, and exit cleanly in real command-line tools.

> **Quick answer:** In Node.js, `process` is the built-in object that represents the running program. Use `process.argv` for CLI arguments, `process.env` for environment variables, `process.exitCode` or `process.exit()` for exit status, and `process.stdin`/`process.stdout`/`process.stderr` for terminal input and output.

> **Difficulty:** Beginner
>
> **You'll understand this better if you know:** basic JavaScript variables, arrays, functions, and how to run a Node.js file from the terminal.

## 1. What Is Node.js process and CLI?

The `process` object is a global Node.js API that describes the current running program. When you run a script from the command line, Node exposes details about the command, the arguments, the environment, the current directory, and the input/output streams attached to that process.

- `process.argv` contains the command used to start Node plus any extra CLI arguments.
- `process.env` gives access to environment variables.
- `process.stdin`, `process.stdout`, and `process.stderr` connect your program to the terminal.
- `process.exitCode` and `process.exit()` control how the program ends.
- `process.cwd()` returns the current working directory where the command was launched.

In practice, this is what turns a plain JavaScript file into a usable command-line tool.

## 2. Why Node.js process Matters

Most Node.js utilities need to read commands from the user, print results, and fail with meaningful exit statuses. That is all managed through `process`.

Without it, your script would not know:

- what file or option the user typed after the command name,
- which environment it is running in,
- whether output should go to standard output or standard error,
- how to signal success or failure to another tool.

This matters for task runners, deployment scripts, build tools, linters, test commands, and small automation utilities.

## 3. Basic Syntax

The simplest way to use `process` is to inspect its properties directly. A CLI program usually starts by reading arguments from `process.argv` and then decides what to do.

### Minimal example

This example prints the arguments that were passed after the file name.

```javascript
const args = process.argv.slice(2);

console.log("Arguments:", args);
```

The first two entries in `process.argv` are the Node executable path and the script path. That is why `slice(2)` is so common in CLI code.

### Common process properties

These properties are the ones you will use most often in command-line programs.

```javascript
const cwd = process.cwd();
const platform = process.platform;
const nodeVersion = process.version;
const envMode = process.env.NODE_ENV;
```

This shows how `process` combines runtime metadata with shell data.

## 4. Step-by-Step Examples

### Example 1: Read a name from the command line

This example accepts a name after the script file and prints a greeting.

```javascript
const name = process.argv.slice(2).join(" ");

if (!name) {
  console.error("Usage: node greet.js <name>");
  process.exitCode = 1;
} else {
  console.log(`Hello, ${name}!`);
}
```

The script reads text after the command name, checks whether the user provided anything, and sets a failing exit code when the input is missing.

### Example 2: Read an environment variable

Environment variables are a common way to pass configuration into CLI tools.

```javascript
const apiKey = process.env.API_KEY;

if (!apiKey) {
  console.error("Missing API_KEY environment variable");
  process.exitCode = 1;
} else {
  console.log("API key loaded");
}
```

This pattern is useful when you want to avoid hardcoding secrets into source code.

### Example 3: Print to stdout and stderr

Normal output and error output should go to different streams so shell tools can separate them.

```javascript
const input = "config.json";

if (input === "config.json") {
  process.stdout.write("Reading config...\n");
} else {
  process.stderr.write("File not found\n");
}
```

`stdout` is for expected output, while `stderr` is for problems and diagnostics.

### Example 4: Exit with an explicit code

Exit codes tell other programs whether your command succeeded.

```javascript
const ok = true;

if (ok) {
  process.exitCode = 0;
} else {
  process.exitCode = 1;
}
```

Using `exitCode` is usually safer than calling `process.exit()` immediately because it allows buffered output to finish.

## 5. Practical Use Cases

- Build a script that accepts filenames, flags, or search terms from the terminal.
- Write a deployment helper that reads credentials from environment variables.
- Create a test or lint wrapper that exits with a failing status when checks fail.
- Pipe data from one command into another by reading from standard input.
- Produce machine-readable logs for shell pipelines by writing important output to `stdout` and errors to `stderr`.
- Detect the current working directory when a tool needs to locate project files relative to where it was launched.

## 6. Common Mistakes

### Mistake 1: Forgetting that argv includes Node and the script path

New CLI authors often treat the first argument as the user value, but `process.argv` always starts with runtime paths.

**Problem:** This code reads the wrong value because index 0 and 1 are not user arguments. The greeting can become the script path instead of the intended name.

```javascript
const name = process.argv[1];
console.log(`Hello, ${name}!`);
```

**Fix:** Skip the first two entries and then read the user arguments.

```javascript
const name = process.argv.slice(2).join(" ");
console.log(`Hello, ${name}!`);
```

The corrected version works because it reads only the values that the user actually typed after the command.

### Mistake 2: Using process.exit() too early

`process.exit()` ends the process immediately, which can interrupt pending writes or cleanup work.

**Problem:** This can cause logs to disappear or file output to stop before the buffer is flushed.

```javascript
console.log("Done");
process.exit(0);
```

**Fix:** Prefer `process.exitCode` and let Node finish naturally when possible.

```javascript
console.log("Done");
process.exitCode = 0;
```

The corrected version is safer because Node can complete queued output before the program ends.

### Mistake 3: Writing errors to stdout

Shell users often redirect normal output separately from error output. If you print everything to `stdout`, automation becomes harder.

**Problem:** Error messages mixed into standard output can break scripts that expect clean data.

```javascript
if (!process.env.TOKEN) {
  console.log("TOKEN is missing");
}
```

**Fix:** Send problems to `stderr` and set a failing exit code.

```javascript
if (!process.env.TOKEN) {
  process.stderr.write("TOKEN is missing\n");
  process.exitCode = 1;
}
```

The corrected version is easier to automate because standard output stays clean for real data.

## 7. Best Practices

### Practice 1: Parse arguments once and keep them in variables

Repeatedly reading `process.argv` makes code harder to understand and test. Collect the values near the top of the file and use them consistently.

```javascript
const [, , command, target] = process.argv;
```

This makes it clearer which values the script expects.

### Practice 2: Use exit codes intentionally

CLI tools are often chained together. A nonzero exit code is the standard way to tell the shell that something went wrong.

```javascript
if (!target) {
  process.stderr.write("Missing target\n");
  process.exitCode = 1;
}
```

This helps other tools detect failures reliably.

### Practice 3: Keep secrets out of source files

Use environment variables for passwords, tokens, and API keys instead of hardcoding them into your script.

```javascript
const token = process.env.TOKEN;
```

This is safer, easier to deploy across environments, and simpler to rotate when secrets change.

## 8. Limitations and Edge Cases

- `process.argv` is raw text. It does not automatically handle flags, quoted values, or type conversion.
- `process.env` values are always strings or `undefined`; you must convert numbers and booleans yourself.
- `stdout` and `stderr` may be buffered, so immediately ending the process can cut off output in some cases.
- `process.cwd()` is based on where the command was launched, not where the script file lives.
- Windows shells, POSIX shells, and package manager wrappers can pass arguments slightly differently, especially when quotes and spaces are involved.
- When a script is run through another tool, the visible command line may differ from the real Node invocation, which can make argument debugging confusing.

## 9. Practical Mini Project

Here is a small but complete file renaming tool. It reads a source name and destination name from the command line, checks that both are present, and reports success or failure with the right streams and exit code.

```javascript
const fs = require("node:fs/promises");

async function main() {
  const [, , source, destination] = process.argv;

  if (!source || !destination) {
    process.stderr.write("Usage: node rename.js <source> <destination>\n");
    process.exitCode = 1;
    return;
  }

  try {
    await fs.rename(source, destination);
    process.stdout.write(`Renamed ${source} to ${destination}\n`);
  } catch (error) {
    process.stderr.write(`Rename failed: ${error.message}\n`);
    process.exitCode = 1;
  }
}

main();
```

This project combines the main ideas in one place: argument parsing, exit status, terminal output, and error handling. It is the shape of many real-world Node CLI tools.

## 10. Key Points

- `process.argv` stores the Node command, the script path, and then the user arguments.
- `process.env` is the standard way to read environment configuration in CLI apps.
- `stdout` is for regular output and `stderr` is for errors and diagnostics.
- `process.exitCode` is usually safer than calling `process.exit()` directly.
- `process.cwd()` tells you where the command was launched, which is not always the same as the script folder.

## 11. Practice Exercise

- Write a Node.js script that accepts two numbers from the command line.
- Print their sum to standard output.
- If either number is missing or invalid, print an error message to standard error and exit with code 1.

**Expected output:** Running `node add.js 7 5` should print `12`. Running it with missing input should show a usage message and a failing exit code.

**Hint:** Use `process.argv.slice(2)`, convert the strings with `Number()`, and check `Number.isNaN()`.

**Solution:**

```javascript
const [aText, bText] = process.argv.slice(2);

const a = Number(aText);
const b = Number(bText);

if (!aText || !bText || Number.isNaN(a) || Number.isNaN(b)) {
  process.stderr.write("Usage: node add.js <number> <number>\n");
  process.exitCode = 1;
} else {
  process.stdout.write(`${a + b}\n`);
}
```

This solution works because it separates parsing, validation, output, and failure handling in a predictable CLI flow.

## 12. Final Summary

Node.js `process` is the bridge between your JavaScript code and the command line. It gives you access to arguments, environment variables, current directory information, standard streams, and exit behavior, which are the building blocks of every practical CLI tool.

Once you understand `process.argv`, `process.env`, `stdout`, `stderr`, and exit codes, you can build scripts that work well with shells, automation, and other programs. The next step is to combine these basics with robust argument parsing and file system APIs to create more capable command-line tools.
