Top 40 Node.js Interview Questions
Essential Node.js interview questions covering the event loop, modules, Express.js, REST APIs, and more. Asked at companies hiring for backend, full-stack, and MERN stack developer roles.
Node.js Basics
Q1–Q15Q1What is Node.js? How is it different from browser JavaScript?Easy▼
Node.js is a runtime environment that lets you run JavaScript on the server (outside a browser). It's built on Chrome's V8 engine.
Node.js vs Browser JS:
- Node has
fs,http,pathmodules — browser has DOM, window, fetch - Node uses CommonJS (
require) — browser uses ES modules (import) — Node 18+ supports both - No DOM in Node; no
documentorwindow - Node controls the runtime version; browser varies by user
Q2What is the Node.js event loop?Hard▼
The event loop is what makes Node.js non-blocking despite being single-threaded. It processes tasks in phases:
- timers —
setTimeout,setIntervalcallbacks - pending callbacks — I/O callbacks deferred to next iteration
- idle/prepare — internal use
- poll — retrieve new I/O events; execute I/O callbacks
- check —
setImmediatecallbacks - close callbacks — e.g., socket.on('close', ...)
Between each phase, Node runs the microtask queue (Promise callbacks, process.nextTick).
console.log("1");
setTimeout(() => console.log("2"), 0);
setImmediate(() => console.log("3"));
process.nextTick(() => console.log("4"));
Promise.resolve().then(() => console.log("5"));
console.log("6");
// Output: 1, 6, 4, 5, 2 or 3 (2/3 order depends on context)
Q3What is process.nextTick()?Hard▼
process.nextTick() queues a callback to run at the end of the current operation, before the event loop continues — even before Promises.
process.nextTick(() => console.log("nextTick"));
Promise.resolve().then(() => console.log("Promise"));
console.log("sync");
// Output: sync → nextTick → Promise
Use sparingly — excessive nextTick calls can starve the event loop (block I/O operations).
Q4What are Node.js modules? What is the difference between CommonJS and ES modules?Medium▼
// CommonJS (traditional Node.js)
const fs = require("fs");
const { add } = require("./math");
module.exports = { myFunction };
// ES Modules (modern — Node 12+, use .mjs or "type":"module" in package.json)
import fs from "fs";
import { add } from "./math.js";
export { myFunction };
Key differences: CommonJS loads synchronously, ES modules load asynchronously. CommonJS is dynamic (require can be in an if block), ES modules are static (analysed at parse time — enables tree-shaking).
Q5What is npm? What is the difference between dependencies and devDependencies?Easy▼
// dependencies — needed in production
npm install express // saved to dependencies
// devDependencies — only needed during development/build
npm install --save-dev nodemon // saved to devDependencies
// package.json
{
"dependencies": {
"express": "^4.18.0" // needed at runtime
},
"devDependencies": {
"nodemon": "^3.0.0", // dev server — not in production
"jest": "^29.0.0" // test runner — not in production
}
}
Q6What is the difference between npm install and npm ci?Medium▼
npm install: installs packages, updatespackage-lock.json, resolves version ranges. Use during development.npm ci: clean install — deletesnode_modulesfirst, installs exact versions frompackage-lock.json, never updates lock file. Faster, deterministic. Use in CI/CD pipelines.
Q7What is the fs module? Sync vs async file operations?Medium▼
const fs = require("fs");
const fsPromises = require("fs/promises");
// Synchronous — blocks event loop! Only for startup/scripts
const data = fs.readFileSync("file.txt", "utf8");
// Callback-based async
fs.readFile("file.txt", "utf8", (err, data) => {
if (err) throw err;
console.log(data);
});
// Promise-based (modern — recommended)
const data = await fsPromises.readFile("file.txt", "utf8");
await fsPromises.writeFile("out.txt", "Hello");
await fsPromises.appendFile("log.txt", "Entry\n");
Always use async file operations in a server — sync calls block all other requests.
Q8What are streams in Node.js?Hard▼
Streams process data piece by piece instead of loading everything into memory. Essential for large files.
const fs = require("fs");
// Read large file as a stream — memory efficient
const readStream = fs.createReadStream("large.csv");
const writeStream = fs.createWriteStream("output.csv");
readStream.pipe(writeStream); // pipe readable → writable
// Manual handling
readStream.on("data", chunk => console.log("Chunk:", chunk.length));
readStream.on("end", () => console.log("Done"));
readStream.on("error", err => console.error(err));
4 types: Readable, Writable, Duplex (both), Transform (modify data in flight — e.g., gzip).
Q9What is the path module?Easy▼
const path = require("path");
path.join("users", "priya", "docs"); // "users/priya/docs" (OS-agnostic)
path.resolve("./src", "../config"); // absolute path
path.basename("/home/priya/file.txt"); // "file.txt"
path.dirname("/home/priya/file.txt"); // "/home/priya"
path.extname("index.html"); // ".html"
path.parse("/home/priya/file.txt"); // { root, dir, base, name, ext }
// Common pattern — resolve relative to current file
const filePath = path.join(__dirname, "data", "users.json");
Q10What is __dirname and __filename?Easy▼
__filename // absolute path of the current file // e.g., "/home/priya/project/server.js" __dirname // absolute path of the current directory // e.g., "/home/priya/project" // Useful for reliable file paths const config = require(path.join(__dirname, "config.json"));
Note: __dirname and __filename are not available in ES modules. Use import.meta.url and the url module instead.
Q11What is the http module? Build a basic HTTP server.Medium▼
const http = require("http");
const server = http.createServer((req, res) => {
if (req.url === "/" && req.method === "GET") {
res.writeHead(200, { "Content-Type": "text/json" });
res.end(JSON.stringify({ message: "Hello World" }));
} else {
res.writeHead(404);
res.end(JSON.stringify({ error: "Not found" }));
}
});
server.listen(3000, () => {
console.log("Server running on http://localhost:3000");
});
In practice, use Express which wraps the http module with a better API.
Q12What is the difference between setImmediate() and setTimeout(fn, 0)?Hard▼
// Both schedule a callback "soon" but in different phases:
setTimeout(() => console.log("timeout"), 0);
setImmediate(() => console.log("immediate"));
// Inside an I/O callback — setImmediate ALWAYS runs first:
fs.readFile("file", () => {
setTimeout(() => console.log("timeout"), 0);
setImmediate(() => console.log("immediate")); // always first
});
// Outside I/O — order is non-deterministic (depends on system)
// But setImmediate is designed to run AFTER I/O events
Q13What is the EventEmitter in Node.js?Medium▼
const { EventEmitter } = require("events");
class OrderService extends EventEmitter {
placeOrder(order) {
// do work...
this.emit("orderPlaced", order); // fire event
}
}
const service = new OrderService();
service.on("orderPlaced", (order) => {
console.log("Send email for order:", order.id);
});
service.on("orderPlaced", (order) => {
console.log("Update inventory for:", order.id);
});
service.placeOrder({ id: 123, item: "laptop" });
Many Node.js core modules (http, fs streams) extend EventEmitter.
Q14What is a Buffer in Node.js?Medium▼
A Buffer is a fixed-size raw binary data storage. Used for handling binary data (files, network packets, images) where strings aren't appropriate.
// Create buffers
const buf1 = Buffer.from("Hello"); // from string
const buf2 = Buffer.from([72, 101, 108]); // from byte array
const buf3 = Buffer.alloc(10); // 10 zero bytes
// Convert
buf1.toString("utf8"); // "Hello"
buf1.toString("hex"); // "48656c6c6f"
buf1.toString("base64"); // "SGVsbG8="
buf1.length; // 5 (bytes, not chars)
Q15How does Node.js handle errors?Medium▼
// 1. try/catch for sync and async/await
try {
const data = await fs.promises.readFile("missing.txt");
} catch (err) {
console.error(err.code); // 'ENOENT'
}
// 2. Callback error-first pattern
fs.readFile("file.txt", (err, data) => {
if (err) { handle(err); return; }
process(data);
});
// 3. Uncaught exception handler (last resort)
process.on("uncaughtException", (err) => {
console.error("Uncaught:", err);
process.exit(1); // must exit — state may be corrupt
});
// 4. Unhandled promise rejections
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled Rejection:", reason);
});
Express.js & REST APIs
Q16–Q30Q16What is Express.js? Why use it over plain Node.js?Easy▼
Express is a minimal web framework for Node.js that makes building web servers and APIs much simpler.
What Express adds over raw Node http:
- Routing (
app.get,app.post) - Middleware pipeline
- Request/response helpers (
res.json(),req.params,req.body) - Template engine support
- Error handling middleware
const express = require("express");
const app = express();
app.use(express.json()); // parse JSON bodies
app.get("/users", (req, res) => res.json([{ id: 1, name: "Priya" }]));
app.listen(3000, () => console.log("Server on port 3000"));
Q17What is middleware in Express?Medium▼
Middleware is a function that runs between a request arriving and a response being sent. Each middleware can modify req/res, call next() to continue, or end the chain.
// Middleware signature: (req, res, next)
function logger(req, res, next) {
console.log(`${req.method} ${req.url}`);
next(); // must call next or response hangs
}
// Apply globally
app.use(logger);
// Apply to specific route
app.get("/protected", authMiddleware, (req, res) => {
res.json({ data: "secret" });
});
// Built-in middleware
app.use(express.json()); // parse JSON body
app.use(express.static("public")); // serve static files
Q18What is the difference between app.use() and app.get()?Easy▼
// app.use() — matches any HTTP method, partial path match
app.use("/api", apiRouter); // matches /api, /api/users, /api/anything
app.use(express.json()); // applies to all routes
// app.get() — only GET requests, exact path match
app.get("/users", getUsers); // only GET /users
// Other methods
app.post("/users", createUser); // POST
app.put("/users/:id", updateUser);// PUT
app.delete("/users/:id", delUser);// DELETE
app.patch("/users/:id", patchUser);// PATCH
Q19What are req.params, req.query, and req.body?Easy▼
// Route: GET /users/:id/posts?page=2&limit=10
app.get("/users/:id/posts", (req, res) => {
req.params.id; // "42" (from URL path :id)
req.query.page; // "2" (from ?page=2)
req.query.limit; // "10"
});
// POST /users with JSON body { "name": "Priya" }
app.post("/users", (req, res) => {
req.body.name; // "Priya" (requires express.json() middleware)
});
// req.headers — HTTP headers
req.headers["authorization"]; // "Bearer abc123"
Q20What are REST API conventions?Medium▼
// Resource: /users GET /users // list all users → 200 OK POST /users // create a user → 201 Created GET /users/:id // get one user → 200 or 404 PUT /users/:id // replace user → 200 or 404 PATCH /users/:id // partial update → 200 or 404 DELETE /users/:id // delete user → 200 or 404 // Nested resources GET /users/:id/posts // user's posts POST /users/:id/posts // create post for user // HTTP status codes 200 OK, 201 Created, 204 No Content 400 Bad Request, 401 Unauthorized, 403 Forbidden 404 Not Found, 409 Conflict, 422 Unprocessable Entity 500 Internal Server Error
Q21How do you handle errors in Express?Medium▼
// Error-handling middleware — 4 params (err, req, res, next)
// Must be defined LAST
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: err.message || "Internal Server Error"
});
});
// Pass errors to it via next(err)
app.get("/users/:id", async (req, res, next) => {
try {
const user = await User.findById(req.params.id);
if (!user) return next({ status: 404, message: "User not found" });
res.json(user);
} catch (err) {
next(err); // passes to error middleware
}
});
Q22What is CORS? How do you enable it in Express?Medium▼
CORS (Cross-Origin Resource Sharing) is a browser security feature that blocks requests to a different origin (domain/port). The server must explicitly allow cross-origin requests.
const cors = require("cors");
// Allow all origins
app.use(cors());
// Allow specific origin
app.use(cors({
origin: "https://myapp.com",
methods: ["GET", "POST", "PUT", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization"]
}));
// Per-route
app.get("/public", cors(), (req, res) => res.json({ data: "public" }));
Q23What is JWT authentication?Hard▼
JWT (JSON Web Token) is a self-contained token used for stateless authentication. It has 3 parts: Header.Payload.Signature (Base64 encoded).
const jwt = require("jsonwebtoken");
const SECRET = process.env.JWT_SECRET;
// Login — generate token
app.post("/login", async (req, res) => {
const user = await findUser(req.body.email, req.body.password);
if (!user) return res.status(401).json({ error: "Invalid credentials" });
const token = jwt.sign({ id: user.id, role: user.role }, SECRET, { expiresIn: "7d" });
res.json({ token });
});
// Protected route — verify token
function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(" ")[1]; // "Bearer TOKEN"
if (!token) return res.status(401).json({ error: "No token" });
try {
req.user = jwt.verify(token, SECRET);
next();
} catch {
res.status(401).json({ error: "Invalid token" });
}
}
app.get("/profile", authMiddleware, (req, res) => res.json(req.user));
Q24What is Express Router?Medium▼
// routes/users.js
const router = require("express").Router();
router.get("/", getUsers);
router.post("/", createUser);
router.get("/:id", getUser);
router.put("/:id", updateUser);
router.delete("/:id",deleteUser);
module.exports = router;
// app.js
const userRouter = require("./routes/users");
app.use("/users", userRouter);
// → /users, /users/:id all handled by router
Routers keep your code organized. Each resource gets its own file.
Q25What is rate limiting? How do you implement it?Medium▼
const rateLimit = require("express-rate-limit");
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // max 100 requests per window
message: { error: "Too many requests, please try again later" },
standardHeaders: true, // return rate limit info in headers
});
// Apply globally
app.use(limiter);
// Stricter limit for login route (prevent brute force)
const loginLimiter = rateLimit({ windowMs: 60000, max: 5 });
app.post("/login", loginLimiter, loginHandler);
Q26What is input validation in Node.js? Mention a library.Medium▼
// Using Joi — popular validation library
const Joi = require("joi");
const userSchema = Joi.object({
name: Joi.string().min(2).max(50).required(),
email: Joi.string().email().required(),
age: Joi.number().min(18).max(120),
password: Joi.string().min(8).required(),
});
app.post("/users", (req, res) => {
const { error, value } = userSchema.validate(req.body);
if (error) return res.status(400).json({ error: error.details[0].message });
// proceed with valid value
});
Q27How do you connect Node.js to MongoDB?Medium▼
// Using Mongoose
const mongoose = require("mongoose");
await mongoose.connect(process.env.MONGO_URI);
// Define schema
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
createdAt: { type: Date, default: Date.now }
});
const User = mongoose.model("User", userSchema);
// CRUD
const user = await User.create({ name: "Priya", email: "p@test.com" });
const users = await User.find({});
const found = await User.findById(id);
const updated = await User.findByIdAndUpdate(id, { name: "Kumar" }, { new: true });
await User.findByIdAndDelete(id);
Q28What is environment variable management in Node.js?Easy▼
// .env file (never commit this!)
PORT=3000
MONGO_URI=mongodb://localhost:27017/myapp
JWT_SECRET=supersecretkey123
// Load with dotenv
require("dotenv").config();
// Access
process.env.PORT; // "3000"
process.env.MONGO_URI; // "mongodb://..."
process.env.JWT_SECRET; // "supersecretkey123"
// Use in app
const PORT = process.env.PORT || 3000;
app.listen(PORT);
Add .env to .gitignore. Use .env.example (with dummy values) to show what variables are needed.
Q29What is the difference between SQL and NoSQL databases?Medium▼
- SQL (MySQL, PostgreSQL): structured tables with strict schema, ACID transactions, good for relational data with complex queries. Use
mysql2orpg(node-postgres). - NoSQL (MongoDB, Redis): flexible documents/key-value, horizontally scalable, good for rapidly changing data or unstructured data. Use Mongoose or native driver.
When to use MongoDB with Node: MERN stack apps, flexible data models, rapid prototyping.
When to use PostgreSQL with Node: financial data, complex relationships, transactions.
Q30What is a REST API? How do you build a basic CRUD API with Express?Medium▼
const express = require("express");
const app = express();
app.use(express.json());
let users = [{ id: 1, name: "Priya" }];
let nextId = 2;
// GET all
app.get("/users", (req, res) => res.json(users));
// GET one
app.get("/users/:id", (req, res) => {
const user = users.find(u => u.id === +req.params.id);
if (!user) return res.status(404).json({ error: "Not found" });
res.json(user);
});
// POST create
app.post("/users", (req, res) => {
const user = { id: nextId++, name: req.body.name };
users.push(user);
res.status(201).json(user);
});
// PUT update
app.put("/users/:id", (req, res) => {
const idx = users.findIndex(u => u.id === +req.params.id);
if (idx === -1) return res.status(404).json({ error: "Not found" });
users[idx] = { id: +req.params.id, ...req.body };
res.json(users[idx]);
});
// DELETE
app.delete("/users/:id", (req, res) => {
users = users.filter(u => u.id !== +req.params.id);
res.status(204).send();
});
app.listen(3000);
Advanced Node.js
Q31–Q40Q31What is clustering in Node.js?Hard▼
Node.js is single-threaded — it uses only one CPU core. Clustering creates child processes (workers) that share a TCP port, utilizing all cores.
const cluster = require("cluster");
const os = require("os");
if (cluster.isPrimary) {
const numCPUs = os.cpus().length;
for (let i = 0; i < numCPUs; i++) cluster.fork();
cluster.on("exit", (worker) => {
console.log(`Worker ${worker.process.pid} died. Restarting...`);
cluster.fork(); // restart dead worker
});
} else {
// Each worker runs the server
require("./server");
}
In production, use PM2 (pm2 start server.js -i max) which handles clustering automatically.
Q32What is the Worker Threads module?Hard▼
Worker Threads run JavaScript in separate threads (true parallelism) for CPU-intensive tasks, without the overhead of child processes.
const { Worker, isMainThread, parentPort, workerData } = require("worker_threads");
if (isMainThread) {
// Main thread
const worker = new Worker(__filename, { workerData: { n: 40 } });
worker.on("message", result => console.log("Fibonacci:", result));
} else {
// Worker thread
function fib(n) { return n <= 1 ? n : fib(n-1) + fib(n-2); }
parentPort.postMessage(fib(workerData.n));
}
Cluster vs Worker Threads: Cluster = multiple processes (for I/O and web servers). Worker Threads = multiple threads in one process (for CPU-heavy computation).
Q33What is Redis? How is it used with Node.js?Medium▼
Redis is an in-memory key-value store used for caching, session storage, and pub/sub messaging.
const { createClient } = require("redis");
const client = createClient();
await client.connect();
// Caching API response
app.get("/users", async (req, res) => {
const cached = await client.get("users");
if (cached) return res.json(JSON.parse(cached));
const users = await User.find();
await client.setEx("users", 300, JSON.stringify(users)); // cache 5 min
res.json(users);
});
// Session storage, rate limiting, job queues
await client.set("session:abc123", userId, { EX: 3600 });
Q34What is WebSocket? How do you use Socket.io with Node.js?Hard▼
WebSocket is a protocol for persistent, bidirectional communication between client and server. Socket.io is a library built on top of WebSocket with fallbacks and extra features.
// Server (Node.js)
const { Server } = require("socket.io");
const io = new Server(httpServer, { cors: { origin: "*" } });
io.on("connection", (socket) => {
console.log("Connected:", socket.id);
socket.on("message", (data) => {
io.emit("message", data); // broadcast to all clients
});
socket.on("disconnect", () => console.log("Disconnected"));
});
// Client (browser)
// const socket = io("http://localhost:3000");
// socket.on("message", data => console.log(data));
// socket.emit("message", { text: "Hello!" });
Q35What is a job queue? Name a Node.js library for it.Hard▼
A job queue offloads heavy tasks (email sending, image processing, report generation) to background workers so the API responds fast.
// Using BullMQ (Redis-based job queue)
const { Queue, Worker } = require("bullmq");
// Producer — add job to queue
const emailQueue = new Queue("emails");
await emailQueue.add("sendWelcome", { to: "priya@example.com" });
// Consumer — process jobs in background
const worker = new Worker("emails", async job => {
if (job.name === "sendWelcome") {
await sendEmail(job.data.to);
}
});
worker.on("completed", job => console.log(`Job ${job.id} done`));
worker.on("failed", (job, err) => console.error(`Job ${job.id} failed`, err));
Q36What is the difference between spawn, exec, and fork in child processes?Hard▼
const { spawn, exec, fork } = require("child_process");
// exec — runs command in shell, buffers output (small output only)
exec("ls -la", (err, stdout) => console.log(stdout));
// spawn — streams output, no shell overhead (large output)
const ls = spawn("ls", ["-la"]);
ls.stdout.on("data", data => console.log(data.toString()));
// fork — spawns a Node.js process, IPC channel built in
const child = fork("./worker.js");
child.send({ task: "compute" });
child.on("message", result => console.log(result));
Q37What is Helmet.js and why should you use it?Medium▼
Helmet sets HTTP security headers to protect against common web vulnerabilities.
const helmet = require("helmet");
app.use(helmet()); // sets 14 security headers
// What helmet sets:
// X-Content-Type-Options: nosniff (prevent MIME sniffing)
// X-Frame-Options: DENY (prevent clickjacking)
// X-XSS-Protection: 0 (disable outdated XSS filter)
// Content-Security-Policy: ... (prevent XSS)
// Strict-Transport-Security: ... (force HTTPS)
// Referrer-Policy: no-referrer
// And more...
Always use Helmet in production Express apps.
Q38What is PM2? Why use it in production?Medium▼
# Install npm install -g pm2 # Start app pm2 start server.js --name "api" # Cluster mode — use all CPU cores pm2 start server.js -i max # Auto-restart on crash pm2 save pm2 startup # auto-start on system reboot # Monitor pm2 status pm2 logs pm2 monit
PM2 handles: process restart on crash, clustering, log management, graceful reloads (zero downtime), and memory limits.
Q39What is GraphQL? How does it differ from REST?Hard▼
GraphQL is an API query language where the client specifies exactly what data it needs.
- REST: multiple endpoints, fixed responses, may over-fetch or under-fetch data
- GraphQL: single endpoint (
/graphql), client asks for exactly what it needs, strongly typed schema
// GraphQL query
query {
user(id: "42") {
name
email
posts {
title
}
}
}
// Returns exactly:
// { user: { name, email, posts: [{ title }] } }
// No more, no less
Use REST for simple CRUD APIs. Use GraphQL for complex data requirements or when the client varies (mobile vs web).
Q40What are some Node.js security best practices?Hard▼
- Use Helmet.js — sets security HTTP headers
- Validate all input — use Joi or Zod; never trust user data
- Parameterized queries — prevent SQL injection (never concatenate user input into SQL)
- Rate limiting — prevent brute force and DDoS
- Store secrets in env vars — never hardcode API keys or passwords
- Hash passwords — use bcrypt, never store plain text
- Use HTTPS — never transmit sensitive data over HTTP
- Keep dependencies updated — run
npm auditregularly - Least privilege — run Node with minimal OS permissions
- Log and monitor — detect attacks early