ESM Modules
Import / Export
// enable ESM: package.json → "type": "module" (or .mjs)
export function add(a, b) { return a + b; } // named
export const PI = 3.14159;
export default multiply; // default
import multiply, { add, PI } from './math.js'; // need .js
import * as math from './math.js'; // namespace
import { readFile } from 'node:fs/promises'; // built-in
const mod = await import('./lazy.js'); // dynamic
// top-level await + ESM globals
const cfg = JSON.parse(await readFile('./c.json', 'utf8'));
import.meta.dirname; import.meta.url; // no __dirname
Event Loop
Phases & Priority
| Order | Runs |
| 1. sync | Current call stack |
| 2. nextTick | process.nextTick queue |
| 3. microtasks | Resolved Promise .then |
| 4. timers | setTimeout / setInterval |
| 5. poll | I/O callbacks |
| 6. check | setImmediate |
| 7. close | 'close' events |
package.json & semver
Key Fields
{
"type": "module",
"engines": { "node": ">=20" },
"scripts": { "start": "node src/server.js",
"test": "node --test",
"dev": "node --watch src/server.js" },
"dependencies": { "express": "^5.0.0" },
"devDependencies": { "eslint": "~9.10.0" }
}
Version Ranges
| Range | Allows |
| ^1.4.2 | >=1.4.2 <2.0.0 (same major) |
| ~1.4.2 | >=1.4.2 <1.5.0 (same minor) |
| 1.4.2 | Exact pin |
npm Commands
npm install # install + update lockfile
npm ci # reproducible install from lockfile (CI)
npm install pkg@1.2 # add dependency
npm run <script> # run a package.json script
npm outdated # list upgradable deps
npm audit fix # patch known vulnerabilities
Built-in Modules
fs/promises & path
import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises';
import path from 'node:path';
await mkdir('data', { recursive: true });
await writeFile('data/x.txt', 'hi');
const t = await readFile('data/x.txt', 'utf8');
path.join(import.meta.dirname, 'data', 'x.txt');
path.basename(p); path.extname(p); path.resolve(p);
crypto & os
import crypto from 'node:crypto';
import os from 'node:os';
crypto.randomUUID();
crypto.createHash('sha256').update(data).digest('hex');
crypto.createHmac('sha256', key).update(msg).digest('hex');
os.cpus().length; os.freemem(); os.platform(); os.hostname();
http server
import { createServer } from 'node:http';
createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
}).listen(3000);
Streams & Buffers
import { createReadStream, createWriteStream } from 'node:fs';
import { pipeline } from 'node:stream/promises';
import { createGzip } from 'node:zlib';
// handles backpressure + cleanup automatically
await pipeline(
createReadStream('big.log'),
createGzip(),
createWriteStream('big.log.gz')
);
Buffer.from('Node', 'utf8'); // bytes from string
buf.toString('base64'); // encode
Buffer.alloc(16); // zero-filled buffer
| Stream | Use |
| Readable | Source (file read, request) |
| Writable | Sink (file write, response) |
| Transform | In → out (gzip, cipher) |
| Duplex | Both (TCP socket) |
EventEmitter
import { EventEmitter } from 'node:events';
const ee = new EventEmitter();
ee.on('data', (x) => console.log(x)); // listen
ee.once('done', () => {}); // fire once
ee.emit('data', 42); // trigger
ee.off('data', handler); // remove
ee.on('error', (e) => {}); // ALWAYS handle 'error'
process & env
// node --env-file=.env server.js
process.env.PORT; // env vars
process.argv.slice(2); // CLI args
process.platform; // 'darwin' | 'linux' | 'win32'
process.version; // node version
process.exit(1); // exit with code
process.on('SIGTERM', () => { /* graceful shutdown */ });
process.on('unhandledRejection', (e) => { log(e); process.exit(1); });
Async Errors & Testing
try/catch + Promise combinators
try { await risky(); }
catch (err) { throw new Error('context', { cause: err }); }
await Promise.all([a(), b()]); // fail fast, all results
await Promise.allSettled([a(), b()]); // never rejects
await Promise.race([a(), timeout()]); // first to settle
node:test (node --test)
import { test } from 'node:test';
import assert from 'node:assert/strict';
test('adds', () => assert.equal(add(2, 3), 5));
test('rejects', async () => {
await assert.rejects(() => loadUser('nope'));
});
Worker Threads
import { Worker } from 'node:worker_threads';
const w = new Worker('./task.js', { workerData: { n: 40 } });
w.on('message', (r) => console.log(r));
w.on('error', (e) => console.error(e));
// use for CPU-bound work; keep the main loop free