Node.js Interview
Questions & Answers
π±Beginner QuestionsQ1βQ10
Node.js is a JavaScript runtime built on Chrome's V8 engine that lets you run JavaScript on the server β outside the browser. Created by Ryan Dahl in 2009, it was built to solve a problem: traditional web servers like Apache blocked the thread while waiting for I/O (file reads, DB queries), wasting CPU cycles.
- Non-blocking I/O: Node uses an event-driven, asynchronous model β it doesn't wait for slow operations to finish before handling the next request.
- Single language: JavaScript on both front-end and back-end β same team, shared code.
- npm ecosystem: Over 2 million packages β the largest package registry in the world.
- Great for: Real-time apps (chat, live updates), REST APIs, microservices, CLI tools, BFF (Backend For Frontend).
- Not great for: CPU-heavy computation (image processing, video encoding, machine learning).
The event loop is the core mechanism that makes Node.js asynchronous and non-blocking. Node is single-threaded β it can only do one thing at a time β but the event loop lets it handle many operations concurrently by delegating I/O to the OS and processing results when they're ready.
process.nextTick fires before any I/O or timers β even before Promise callbacks. This makes it useful for scheduling something at the very end of the current operation, before anything else asynchronous runs.require) and ES Modules (import)?| Feature | CommonJS (CJS) | ES Modules (ESM) |
|---|---|---|
| Syntax | require() / module.exports | import / export |
| Loading | Synchronous | Asynchronous (static analysis) |
| Tree-shaking | β Not supported | β Supported by bundlers |
| Top-level await | β No | β Yes |
| File extension | .js or .cjs | .mjs or "type":"module" in package.json |
| Default in Node | β Traditional default | Opt-in (Node 12+, stable Node 14+) |
npm (Node Package Manager) is the default package manager for Node.js. It manages dependencies, scripts, and publishes packages to the npm registry.
dependencies vs devDependencies: dependencies are needed at runtime (express, mongoose). devDependencies are only needed during development (jest, eslint, typescript). When deploying, you can run npm install --production to skip devDependencies.Node.js ships with built-in modules that you can require without installing anything. They cover file system, networking, paths, cryptography, and more.
fs.readFileSync in an HTTP handler blocks ALL requests while it runs. One slow file read can freeze your entire server.Express.js is a minimal, unopinionated web framework for Node.js. Node's built-in http module is powerful but low-level β you'd have to manually parse routes, bodies, cookies, and handle errors. Express wraps all of this into a clean, simple API.
Environment variables are key-value pairs stored outside your code β used for configuration that changes between environments (dev, staging, production) or that must be kept secret (API keys, DB passwords).
.env to .gitignore. Committing secrets to a public repository is one of the most common and dangerous developer mistakes. Once pushed, assume the secret is compromised.Callback hell (also called "the pyramid of doom") is when nested callbacks become so deeply indented that the code is hard to read, debug, and maintain.
__dirname and __filename in Node.js?These are global variables available in every CommonJS module (not ESM):
β‘Intermediate QuestionsQ11βQ21
Middleware are functions that have access to the request, response, and next objects. They execute in sequence and form a pipeline β each middleware either ends the request or calls next() to pass control to the next function.
app.use(). Your error handler must be registered LAST (after all routes) and must have exactly 4 parameters to be recognised as an error handler.Streams are objects that let you read or write data in chunks rather than loading everything into memory at once. They're memory-efficient for handling large files, real-time data, or piping between sources.
EventEmitter is Node's implementation of the observer/pub-sub pattern. Objects extend EventEmitter to emit named events, and listeners can subscribe to those events. It's the foundation of Node's streaming and networking APIs.
REST (Representational State Transfer) is an architectural style for APIs. Key principles: stateless, client-server, uniform interface using HTTP methods, resource-based URLs.
JWT (JSON Web Token) is a compact, self-contained way to transmit authentication data. A JWT has three parts: Header.Payload.Signature β base64url encoded and signed with a secret key.
CORS (Cross-Origin Resource Sharing) is a browser security mechanism that blocks web pages from making requests to a different domain than the one that served them. Node.js APIs need to explicitly allow cross-origin requests.
Rate limiting controls how many requests a client can make in a given time window. It protects APIs from abuse, brute-force attacks, and denial-of-service (DoS) overloads.
setImmediate, setTimeout, and process.nextTick?| Function | Queue | Fires when | Use case |
|---|---|---|---|
process.nextTick | nextTick queue (microtask) | Before any I/O, timers, or promises in current iteration | Defer until end of current operation |
Promise.resolve() | Microtask queue | After nextTick, before any I/O | Async operations |
setImmediate | Check phase | After I/O events in current loop iteration | After I/O callbacks |
setTimeout(fn, 0) | Timers phase | After minimum delay (not guaranteed 0ms) | Defer to next loop iteration |
process.nextTick = "right now but after the current function". setImmediate = "as soon as I/O is done". setTimeout(fn,0) = "at the next available timer slot". Prefer setImmediate over setTimeout(fn,0) inside I/O callbacks for more predictable behavior.util.promisify and when would you use it?util.promisify converts a Node.js callback-style function (last argument is (err, result)) into a function that returns a Promise β letting you use it with async/await.
π₯Advanced QuestionsQ22βQ30
Node.js is single-threaded, so by default it only uses one CPU core. The Cluster module lets you spawn multiple Node processes (workers) that share the same server port β each running on a separate core.
pm2 (pm2 start app.js -i max) instead of managing clusters manually β it handles clustering, restarts, logging, and zero-downtime deployments.Worker Threads allow running JavaScript in separate threads within the same process β sharing memory via SharedArrayBuffer. Unlike child processes (Cluster), they're lighter and can share data without serialisation. Ideal for CPU-intensive tasks that would otherwise block the event loop.
| Feature | Cluster | Worker Threads |
|---|---|---|
| Memory | Separate (heavier) | Shared memory available |
| Best for | Handling more HTTP requests | CPU-intensive computation |
| Communication | IPC (serialised) | Messages + SharedArrayBuffer |
WebSockets provide a full-duplex, persistent connection between client and server β unlike HTTP which is request/response. Once connected, both sides can send messages at any time without re-establishing the connection. Perfect for real-time apps.
| Feature | PostgreSQL (SQL) | MongoDB (NoSQL) |
|---|---|---|
| Schema | Fixed schema | Flexible (schema-less) |
| Data model | Tables + rows | Collections + documents (JSON-like) |
| Relationships | β Foreign keys, JOINs | Manual embedding or references |
| ACID | β Full ACID transactions | β Since v4 (with sessions) |
| Best for | Complex relationships, financial data, reporting | Flexible data, rapid iteration, document storage |
Microservices splits an application into small, independent services that each do one thing well, communicate over a network, and can be deployed independently.
- Node.js strengths for microservices: Fast startup time, low memory footprint, great for I/O-bound services, easy HTTP/gRPC server setup.
- Service communication: HTTP/REST, gRPC (faster, typed), or message queues (RabbitMQ, Kafka) for async communication.
Node.js uses V8's garbage collector. Memory leaks occur when references to objects are never released, preventing GC from cleaning them up.
Graceful shutdown ensures that when your Node.js process receives a termination signal (e.g., during a deployment or container restart), it finishes in-flight requests before exiting β rather than abruptly cutting connections.
Up Next: SQL & Databases
You've covered Node.js. Next in the series is SQL β joins, indexes, normalization, transactions, and NoSQL comparisons. Coming very soon.
β HTML & CSS Q&A