Node.js / Basic: the Node.js runtime / Lesson 6
Configuration and command-line programs
Turn text settings into validated configuration and make a small CLI predictable for people, automation, and tests.
On this page
Lesson goals
- Explain the concepts and trade-offs behind configuration and command-line programs.
- Predict and verify both examples, including their outputs and cleanup behavior.
- Diagnose the three common failures and apply the worked solutions to related inputs.
Configuration is input
Environment variables are strings or undefined, not numbers and booleans. The text "false" is truthy in JavaScript, so Boolean(process.env.FEATURE) does not parse a feature flag. Validate settings once at startup and produce a small configuration object for the rest of the application. Distinguish a missing value from an explicitly empty value. Defaults are useful for optional settings, but silently defaulting a required secret or database address can send an application to the wrong system. Configuration errors should fail before the application announces readiness.
Parse a strict contract
Choose allowed syntax and range for every field. For a port, decimal digits and an integer range are clearer than accepting any Number conversion. parseInt can accept a prefix of malformed input such as "12oops", which is often unsuitable for configuration. In the first example port zero is deliberately allowed for local tests; a deployed listener may require a different policy. The feature flag accepts only true or false. Freezing the resulting shallow object discourages accidental mutation, but nested values would need their own design.
Keep the parser independent
The example passes an ordinary object instead of editing the process environment. This makes valid and invalid cases deterministic, prevents tests from interfering with one another, and lets startup call the same function with process.env. Do not scatter environment reads across every module: that hides dependencies and makes configuration changes difficult to audit. Environment-file loading behavior varies with the selected runtime and launch command. Document where settings come from and their precedence, and keep secret values out of source control, sample outputs, error responses, and diagnostic logs.
function config(env) {
const text = env.PORT ?? '0';
if (!/^\d+$/.test(text)) throw new Error('Invalid PORT');
const port = Number(text);
if (!Number.isInteger(port) || port > 65535) throw new Error('Invalid PORT');
const flag = env.FEATURE ?? 'false';
if (!['true', 'false'].includes(flag)) throw new Error('Invalid FEATURE');
return Object.freeze({ port, feature: flag === 'true' });
}
console.log(JSON.stringify(config({ PORT: '3000', FEATURE: 'false' })));
try { config({ PORT: '12oops' }); } catch (error) { console.log(error.message); }
{"port":3000,"feature":false}
Invalid PORT
A false flag remains false, and malformed numeric text is rejected rather than partially parsed.
A CLI has two output channels
A command-line tool receives arguments through process.argv, with the executable and script path occupying the first two entries. Put normal machine-readable results on stdout and diagnostics on stderr. A nonzero exit status tells automation that a command failed. The second example isolates argument interpretation as a function, so it can demonstrate both cases without deliberately terminating the lesson with failure. In a real entry point, catch the validation error, print a concise diagnostic to stderr, and set process.exitCode to one after cleanup.
function run(args) {
if (args.length !== 1 || !/^\d+$/.test(args[0])) throw new Error('Usage: double <integer>');
const value = Number(args[0]);
if (!Number.isSafeInteger(value) || !Number.isSafeInteger(value * 2)) throw new Error('Integer out of range');
return String(value * 2);
}
console.log(run(['8']));
try { run(['eight']); } catch (error) { console.log(error.message); }
16
Usage: double <integer>
The pure argument parser accepts one safe nonnegative integer. An actual entry point would call it with process.argv.slice(2).
Design safe automation boundaries
Arguments and environment values are untrusted inputs even when a shell supplied them. Avoid assembling shell command strings from those values; use direct APIs or an executable with a separate argument array when process creation is necessary. Do not log the entire environment to debug a missing setting. Report the setting name and expected format instead. Add a help mode that explains accepted arguments, make repeated execution safe where possible, and document whether an operation writes files. Predictable parsing and exit behavior make a CLI a dependable component of a larger workflow.
Common errors and fixes
The text false enables a feature
- Why it happens
- Boolean conversion tested string presence rather than content.
- How to fix it
- Accept an explicit allowlist and compare to "true".
- Prevent it next time
- Test missing, empty, true, false, and invalid settings.
A CLI succeeds after printing an error
- Why it happens
- It never set a failing exit status.
- How to fix it
- Set process.exitCode after handling and cleanup.
- Prevent it next time
- Test status as well as output.
An error log exposes a secret
- Why it happens
- The complete environment or configuration object was logged.
- How to fix it
- Remove sensitive values and rotate any exposed credential.
- Prevent it next time
- Use an allowlist of safe diagnostic fields.
Check your understanding
1. A required setting API_KEY is absent. Should startup use an empty string?
Show worked solution
No. Validate presence and required format, then fail startup with a message naming API_KEY without printing a value. Readiness must remain false. An empty fallback would defer failure to a later request and make the problem harder to diagnose.
2. Specify stdout, stderr, and exit status for doubling 8 and for invalid input.
Show worked solution
For 8, write 16 to stdout, leave stderr empty, and exit zero. For invalid input, leave stdout empty, write usage to stderr, and exit one. Keeping channels separate lets a pipeline consume only successful data and detect failure reliably.
Key takeaways
- Environment variables are strings or undefined, not numbers and booleans.
- Choose allowed syntax and range for every field.
- Arguments and environment values are untrusted inputs even when a shell supplied them.