Node.js Basics
10 examples to get you started with Node.js Fundamentals - 7 basic and 3 intermediate.
Prerequisites
- Install Node.js 24.18.0 (Active LTS) via fnm, nvm, or Volta.
- Verify with
node --versionandnpm --version(npm 10+ ships with Node 24). - New projects should use
"type": "module"inpackage.jsonfor ESM by default.
Basic Examples
1. Check Your Node Version
Confirm the runtime before running any script or deploying.
console.log(process.version); // v24.18.0
console.log(process.versions.v8); // V8 engine version
console.log(process.versions.uv); // libuv versionprocess.versionis the authoritative runtime string - log it at startup in production.process.versionsexposes V8, libuv, OpenSSL, and other native bindings.- CI and Docker images should match the version pinned in
package.jsonengines.
Related: Installing & Version Management - pin LTS across teams | Node.js Release & LTS Policy - what production may run
2. Run a Script with node
The simplest way to execute a TypeScript-compiled or plain JavaScript file.
// hello.mjs
console.log('Hello from Node.js', process.version);node hello.mjs- Save as
.mjsor set"type": "module"inpackage.jsonfor ESM syntax. node file.tsworks withtsxin dev; production compiles withtscfirst.- Exit code 0 means success; non-zero signals failure to shells and CI.
Related: Running Scripts & Shebang - make scripts executable
3. Use Built-in fetch
Node 18+ ships a global fetch - no node-fetch package required.
const response = await fetch('https://nodejs.org/dist/index.json');
const releases = await response.json() as Array<{ version: string; lts: string | false }>;
const lts = releases.find((r) => r.lts !== false);
console.log('Current LTS tag:', lts?.lts, lts?.version);fetchreturns Web-standardResponseobjects - same API as browsers.- Always check
response.okorresponse.statusbefore parsing the body. - For streaming large downloads, use
response.bodyas a Web ReadableStream.
Related: How Node.js Works - where I/O runs off the main thread
4. Read Environment Variables
Configuration belongs in the environment, not hard-coded in source.
const port = Number(process.env.PORT ?? 3000);
const nodeEnv = process.env.NODE_ENV ?? 'development';
console.log(`Starting on port ${port} in ${nodeEnv} mode`);process.envis a plain object of string values - coerce types explicitly.- Use
??for defaults; empty string""is truthy and will not fall through||. - Validate required vars at startup with Zod or a schema library before accepting traffic.
5. Import an ES Module
ESM is the forward path - use static import at the top of files.
import { readFile } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const pkg = await readFile(path.join(__dirname, 'package.json'), 'utf8');
console.log(JSON.parse(pkg).name);- Prefix built-in imports with
node:for clarity and future-proofing. import.meta.urlreplaces CJS__filenameand__dirname.- Top-level
awaitis allowed in ESM modules at module scope.
Related: How Node.js Works - V8 and the module loader
6. Handle Errors with try/catch
Async errors in await expressions propagate like synchronous throws.
async function loadConfig(): Promise<Record<string, string>> {
try {
const res = await fetch(process.env.CONFIG_URL!);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json() as Record<string, string>;
} catch (err) {
console.error('Config load failed:', err);
throw err; // re-throw so the process exits or a supervisor restarts
}
}- Unhandled promise rejections can crash Node in production - always attach
.catch()or usetry/catch. - Log the error with context (URL, status) before re-throwing.
- For HTTP servers, map errors to structured JSON responses instead of stack traces.
7. Quick REPL Experiment
Prototype APIs interactively without creating a file.
node> await fetch('https://httpbin.org/get').then(r => r.json())
> Object.keys(process.versions)
> .exit- The REPL supports top-level
awaitwhen started withnode(no-eflag needed in Node 24). - Use
.save filenameto persist a session and.load filenameto replay it. - Tab completion and
.helplist all dot-commands.
Related: The REPL & Quick Experiments - deeper REPL workflows
Intermediate Examples
8. Write a Minimal HTTP Server
node:http is built in - no framework required for health checks and probes.
import { createServer } from 'node:http';
const server = createServer((req, res) => {
if (req.url === '/health') {
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', version: process.version }));
return;
}
res.writeHead(404).end();
});
server.listen(3000, () => console.log('Listening on :3000'));createServercallback runs on every request - keep it non-blocking.- Set
content-typeexplicitly; clients assumetext/plainotherwise. - For production APIs, graduate to Express 5 or Fastify 5 for routing and validation.
9. Run Tests with node:test
Node's built-in test runner needs no Jest install for unit tests.
// math.test.ts
import { test, describe } from 'node:test';
import assert from 'node:assert/strict';
function add(a: number, b: number): number {
return a + b;
}
describe('add', () => {
test('sums two numbers', () => {
assert.equal(add(2, 3), 5);
});
});node --import tsx --test math.test.tsnode:testsupportsdescribe, hooks, and async tests natively.- Use
node:assert/strictfor strict equality checks. - Pair with
tsxin dev for TypeScript; compile before CI if you prefertscoutput.
10. Graceful Process Exit
Listen for signals so containers and orchestrators can drain work cleanly.
let shuttingDown = false;
function shutdown(signal: string): void {
if (shuttingDown) return;
shuttingDown = true;
console.log(`Received ${signal}, draining...`);
setTimeout(() => process.exit(0), 2000);
}
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));- Kubernetes sends
SIGTERMbefore killing a pod - you have ~30 seconds to drain. - Close database pools and HTTP servers before calling
process.exit. - Idempotent shutdown handlers prevent double-close bugs on repeated signals.
Related: How Node.js Works - single-threaded model and signal handling
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.