Chapter 8 of 18 · Node.js for Beginners — Complete Free Tutorial

Paths, Environment and Process

Real programs must find their files, remember their settings and answer to the shell that started them. The Node.js for Beginners series now covers the three tools for that job: the path module for safe path maths, process.env for configuration, and the process object for arguments, exit codes and shutdown. Everything from here on assumes these are second nature.

Why path maths needs a module

Paths use / on macOS and Linux but \ on Windows, and concatenating strings by hand breaks the moment your code runs elsewhere. path does the joining with the platform's own separator.

import path from 'node:path';

console.log(path.join('logs', '2026', 'app.txt'));
console.log(path.join('logs', '../secrets.txt'));
console.log(path.resolve('logs', 'app.txt'));
console.log(path.extname('server.js'));
console.log(path.basename('/etc/config/app.json'));

Expected output:

logs/2026/app.txt
secrets.txt
/Users/asha/projects/logs/app.txt
.js
app.json
  • join — concatenates and normalises ./..; use it for building relative paths.
  • resolve — walks from the current working directory to an absolute path; the last segment that is not absolute is interpreted against process.cwd().
  • parse / dirname / extname — split a path into dir, name and extension.

__dirname and friends

CommonJS files get __dirname (the folder holding the file) and __filename. ESM does not — but the standard equivalent ships in modern Node:

// CommonJS
const base = path.join(__dirname, 'templates');

// ESM ("type": "module")
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
const legacyWay = dirname(fileURLToPath(import.meta.url));  // older Node
console.log(import.meta.dirname);                            // Node 21.2+ ✅

Expected output: the absolute directory of the current file. Always anchor file lookups to the file's directory — process.cwd() changes with every shell command and is a classic "works on my machine" source.

process.env: configuration without hard-coding

Environment variables are strings set outside your code — per machine, per environment, never committed.

PORT=3000 NODE_ENV=development node src/server.js
const port = Number(process.env.PORT ?? 3000);
const dev = process.env.NODE_ENV !== 'production';

console.log(`listening on ${port}, dev=${dev}`);
console.log('home dir:', process.env.HOME ?? process.env.USERPROFILE);

Expected output:

listening on 3000, dev=true
home dir: /Users/asha
VariableTypical purpose
PORTWhich port the server binds — platforms assign one for you
NODE_ENVdevelopment / production — toggles verbose output
DATABASE_URL, API_KEYSecrets that must never be in source control

Node does not read .env files by itself — add dotenv (or node --env-file=.env on Node 20.6+) and keep .env in .gitignore while committing a documented .env.example.

process: arguments, working directory, exit

console.log('args:', process.argv.slice(2));
console.log('cwd :', process.cwd());
console.log('node:', process.version);
console.log('pid :', process.pid);

if (!process.argv[2]) {
  console.error('missing: ');
  process.exitCode = 2;
}

Expected output: argument list, current directory, Node version and the process id; with no argument the script ends with shell status 2 (check with echo $?). Distinct exit codes let scripts and CI distinguish "usage error" from "crash".

Signals and graceful shutdown

Long-running processes are stopped by signals — a Ctrl+C in your terminal sends SIGINT. Production hosts send SIGTERM when redeploying, and a well-built server drains connections instead of dying mid-write:

process.on('SIGTERM', () => {
  console.log('SIGTERM received — closing gracefully');
  server.close(() => process.exit(0));   // stop accepting, finish in-flight
  setTimeout(() => process.exit(1), 5000).unref(); // hard deadline
});

Expected output: on shutdown, the log line prints once, existing requests complete, and the process exits with status 0. Chapter 18 installs this pattern in the project.

Common mistakes

  • String-concatenating paths — dir + '/' + file breaks on Windows; use path.join.
  • Anchoring to cwd() — scripts stop working when run from another folder; anchor to the file.
  • Trusting env types — everything is a string; process.env.PORT === 8080 is never true.
  • Committing secrets — keys in .env pushed to Git must be rotated; use environment variables and ignore files.
  • Calling process.exit() while writing — truncates logs and responses; set exitCode or drain first.

Practice checklist

  1. Build a path from __dirname (or import.meta.dirname) to a data file and read it.
  2. Print path.parse(process.argv[1]) and explain each field.
  3. Run a script with three custom environment variables and read them back with defaults.
  4. Exit with two different codes based on input and verify each in the shell.
  5. Register a SIGINT handler that logs before the process ends.

With files, paths and configuration under your belt, the next safety net is what happens when things go wrong. Next: Errors and Debugging in Node.

Quiz: Paths, Environment and Process

5 questions · 70% to pass

Frequently Asked Questions

Node.js is a runtime that executes JavaScript outside the browser. It combines Google's V8 engine with APIs for files, networks, processes and streams, plus a package ecosystem (npm). You use it to build servers, command-line tools, APIs and anything that needs JavaScript running on a machine rather than in a page.

JavaScript is the language; Node.js is one environment that runs it. In the browser the language comes with a DOM, events and window APIs — in Node those are replaced by the file system, network and process modules. Syntax, functions, arrays, promises and async/await are identical, so your JavaScript knowledge transfers directly.

Browser code is sandboxed: it cannot read your files or open arbitrary ports. Node code has full machine access, which is why it is used for servers and tools but also why you never run untrusted code in it. There is no document or window in Node — the equivalents are global, process and modules.

npm is JavaScript's package manager and public registry. package.json describes your project: name, entry point, dependencies with version ranges, and scripts such as npm run dev. Installing a package copies it into node_modules and records the exact intent in package-lock.json so every machine installs identical versions.

Express is a minimal web framework for Node: a few thousand lines that give you routing, middleware, request parsing and static files on top of the raw HTTP server. It is the most common starting point for REST APIs and the backbone of this tutorial's project chapters.

Both work. CommonJS (require/module.exports) is the classic Node format; ES modules (import/export) are the standard and activated in a package.json with "type": "module". Modern projects favour ESM for browser compatibility, but you will read both in existing code, so this course teaches each.

Middleware are functions that run between the incoming request and the final response, in order: body parsing, logging, authentication, rate limits, error handling. Each receives the request, response and next function and can respond itself or pass control along — Express's pipeline model in one sentence.

A JSON file is perfect for learning CRUD: no setup, readable data, easy to reset. The moment you need concurrent writes, querying or multiple users, move to a real database — MySQL/PostgreSQL for relational data or MongoDB for documents. The storage chapter isolates persistence behind one module so swapping it later is straightforward.

A REST API exposes resources over HTTP using verbs on URLs: GET /api/todos lists, POST creates, PUT or PATCH updates, DELETE removes, and everything answers with JSON plus correct status codes (200, 201, 400, 404, 500). It is stateless — each request carries its own authentication — which makes it easy to cache, scale and consume from any frontend.

Node apps are long-running processes, not static files: deploy them to a Node-friendly host (a VPS, Render, Railway or Fly.io), keep secrets in environment variables, terminate HTTPS at a reverse proxy or platform edge, and send logs somewhere durable. The final chapter covers the full checklist including health checks and graceful shutdown.