Backend · revision notes
Node.js
Runtime, event loop, modules, streams, scaling and npm
1What Node.js is
Definition
Node.js is a JavaScript runtime built on V8 + libuv. It runs JS outside the browser with a single-threaded event loop and non-blocking I/O, so one process can serve many concurrent connections.
RequestEvent loop (1 JS thread)libuv: OS async I/O + thread pool (fs, crypto, dns)Callback queuedResponse
- Great for I/O-heavy work: APIs, real-time apps, proxies.
- Bad at long CPU work on the main thread (image processing, big loops) — it blocks every request. Use
worker_threadsor a separate service.
2The Node event loop phases
- timers —
setTimeout/setIntervalcallbacks - pending callbacks — some deferred system callbacks
- poll — wait for and run I/O callbacks
- check —
setImmediatecallbacks - close —
socket.on("close")etc.
setTimeout(() => console.log('timeout'), 0)
setImmediate(() => console.log('immediate'))
Promise.resolve().then(() => console.log('promise'))
process.nextTick(() => console.log('nextTick'))
console.log('sync')
// sync → nextTick → promise → timeout / immediate
// (timeout vs immediate order varies at top level; inside an I/O callback, immediate runs first)process.nextTick runs before promise microtasks. Too many nextTicks can starve I/O.3Modules and npm
| CommonJS | ES Modules |
|---|---|
require() / module.exports | import / export |
| Loaded synchronously at runtime | Static, analysable, top-level await |
Default for .js without "type": "module" | .mjs, or "type": "module" in package.json |
package.json: scripts, dependencies vs devDependencies,engines.- Semver
MAJOR.MINOR.PATCH:^1.2.3allows minor + patch,~1.2.3allows patch only. - Commit
package-lock.json; usenpm ciin CI for exact, repeatable installs. npxruns a package binary without installing it globally.
4Core modules, streams and errors
import { createReadStream } from 'node:fs'
import { createInterface } from 'node:readline'
const rl = createInterface({ input: createReadStream('access.log') })
let errors = 0
for await (const line of rl) {
if (line.includes(' 500 ')) errors++
}
console.log('5xx responses:', errors)- Core modules:
fs/promises,path,http,crypto,events,stream,os,child_process,worker_threads. - Streams process data chunk by chunk (Readable, Writable, Duplex, Transform) — use
pipeline()for errors and back-pressure. EventEmitter:on,once,emit— the base of streams and servers.- Always handle promise rejections; log
unhandledRejection/uncaughtException, then exit and let the process manager restart.
Common mistake
Sync APIs in request handlers (readFileSync, bcrypt.hashSync, big JSON.parse loops) block every other user. Use the async versions.
5Scaling and production
| Tool | What it does |
|---|---|
| cluster / PM2 | one process per CPU core behind a shared port |
| worker_threads | run CPU-heavy work off the main thread |
| Load balancer + stateless app | scale horizontally across machines |
| Redis | shared cache, sessions, rate limits, queues (BullMQ) |
| Graceful shutdown | on SIGTERM stop accepting, finish in-flight requests, close DB |
- Config from environment variables (12-factor); never commit secrets.
- Structured logs (pino), health checks (
/health), metrics. - Cron jobs: node-cron in a single instance or a queue scheduler to avoid duplicates when scaled.
Revision
Single-threaded event loop + libuv for non-blocking I/O. nextTick → promises → timers/check. Don't block the loop; use streams for big data, worker_threads for CPU, cluster/PM2 + stateless design to scale.
6Rapid-fire Q&A
- Q. Is Node.js single-threaded?
- JavaScript runs on one thread (the event loop), but libuv uses the OS and a thread pool for I/O, crypto and DNS, so I/O doesn't block.
- Q. What blocks the event loop?
- Long synchronous work: CPU-heavy loops, sync file/crypto APIs, huge JSON parsing. Every request waits.
- Q. process.nextTick vs setImmediate vs setTimeout?
- nextTick runs before other microtasks; setImmediate runs in the check phase after I/O; setTimeout(0) runs in the timers phase.
- Q. What are streams and why use them?
- Chunk-by-chunk processing of data (files, HTTP bodies) with low memory and back-pressure handling.
- Q. CommonJS vs ES Modules?
- CommonJS uses require/module.exports and loads synchronously; ESM uses import/export, is statically analysable and supports top-level await.
- Q. How do you scale a Node app?
- cluster/PM2 to use all cores, stateless servers behind a load balancer, Redis for shared state, worker_threads or queues for CPU work.
- Q. dependencies vs devDependencies?
- dependencies are needed at runtime; devDependencies only for building and testing.
- Q. ^ vs ~ in package versions?
- ^1.2.3 allows minor and patch updates; ~1.2.3 allows patch updates only.
- Q. What is graceful shutdown?
- On SIGTERM: stop accepting connections, finish in-flight requests, close DB connections, then exit.