Skip to content

Full Stack with Node.js Interview Questions & Answers

All 200 unique questions in one place — 63 easy, 97 medium, 40 hard — grouped by topic.

Practise by topic

Solved overallStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

What is Node.js and why is it used for backend development?

Save to ▾
Show answer
Node.js is a JavaScript runtime built on the V8 engine that runs JavaScript outside the browser. It uses a single-threaded event loop with non-blocking I/O, so one process can handle thousands of concurrent connections cheaply. It is popular for APIs, real-time apps and microservices because teams can use one language across frontend and backend and reuse a huge npm ecosystem.
02

What is the difference between CommonJS and ES Modules in Node.js?

Save to ▾
Show answer
CommonJS uses require and module.exports, loads modules synchronously and was Node's original system. ES Modules use import and export, are statically analysable, support top-level await and are the JavaScript standard. In Node you enable ESM with the .mjs extension or "type": "module" in package.json. ESM can import CommonJS, but CommonJS needs dynamic import() to load ESM in older versions.
03

What are streams in Node.js and what types exist?

Save to ▾
Show answer
Streams process data in chunks instead of loading everything into memory. There are four types: Readable (fs.createReadStream, HTTP request), Writable (fs.createWriteStream, HTTP response), Duplex (TCP socket, both read and write) and Transform (zlib gzip, modifies data as it passes). Streams keep memory flat when serving large files, uploading media or processing big CSV logs.
04

What is the Buffer class in Node.js?

Save to ▾
Show answer
Buffer represents a fixed-length sequence of raw bytes stored outside the V8 heap. It is used for binary data such as files, images, TCP packets and crypto output. You create one with Buffer.from('text') or Buffer.alloc(size) and convert with toString('utf8' or 'base64'). Buffer.allocUnsafe is faster but may contain old memory, so fill it before use.
05

What is the EventEmitter and how do you use it?

Save to ▾
Show answer
EventEmitter from the events module implements the publish-subscribe pattern. Objects register listeners with on or once and trigger them with emit. Streams, HTTP servers and sockets all extend it. Always handle the 'error' event, because an emitted error without a listener crashes the process. Watch for memory leaks when listeners are added repeatedly; Node warns after 10 listeners by default.
06

Write a simple custom EventEmitter usage for an order service.

Save to ▾
Show answer
const EventEmitter = require('events'); class OrderService extends EventEmitter { place(order) { // save order... this.emit('placed', order); } } const svc = new OrderService(); svc.on('placed', (o) => console.log('Send email for', o.id)); svc.once('placed', () => console.log('First order today')); svc.on('error', (err) => console.error(err)); svc.place({ id: 101 });
07

Create a basic HTTP server in Node.js without any framework.

Save to ▾
Show answer
const http = require('http'); const server = http.createServer((req, res) => { if (req.method === 'GET' && req.url === '/health') { res.writeHead(200, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ status: 'ok' })); } res.writeHead(404); res.end('Not found'); }); server.listen(3000, () => console.log('Listening on 3000'));
08

What is package.json and what is the difference between dependencies and devDependencies?

Save to ▾
Show answer
package.json describes a project: name, version, scripts, entry point and dependencies. dependencies are packages needed at runtime, such as express or mongoose. devDependencies are only for development and build, such as jest, eslint or nodemon. Running npm ci --omit=dev in production installs only runtime packages, which keeps Docker images smaller and reduces attack surface.
09

What is package-lock.json and why should it be committed?

Save to ▾
Show answer
package-lock.json records the exact resolved version and integrity hash of every installed package, including nested ones. Committing it makes installs reproducible across developer machines and CI, so a minor update in a transitive dependency cannot silently break production. In CI use npm ci, which installs strictly from the lock file and fails if it is out of sync with package.json.
10

Explain semantic versioning and the meaning of ^ and ~ in package.json.

Save to ▾
Show answer
Semantic versioning uses MAJOR.MINOR.PATCH: major for breaking changes, minor for new backward-compatible features, patch for bug fixes. A caret like ^4.18.2 allows any 4.x.x at or above 4.18.2. A tilde like ~4.18.2 allows only patch updates, 4.18.x. An exact version pins it. The lock file still decides what is actually installed.
11

A teammate uses fs.readFileSync inside an Express route. What is the problem and what would you suggest?

Save to ▾
Show answer
Synchronous file calls block the event loop for the whole read, so under load every request queues behind disk I/O and latency spikes. In a route, use fs.promises.readFile with await, or stream the file with fs.createReadStream and pipe it to the response. Sync calls are acceptable only at startup, for example reading a config file once before the server listens.
12

Tell me about a time you learned a new backend technology quickly to deliver a feature.

Save to ▾
Show answer
Situation: our team needed real-time order tracking and nobody had used WebSockets. Task: I owned the feature with a two-week deadline. Action: I spent two evenings on the Socket.IO docs, built a small prototype, shared a short demo with the team and then added rooms and Redis adapter for scaling. Result: we shipped on time, and I wrote an internal guide others reused.
13

Explain the Node.js event loop and its main phases.

Save to ▾
Show answer
The event loop lets Node.js run asynchronous callbacks on a single thread. Each iteration passes through phases: timers (setTimeout, setInterval), pending callbacks, poll (waits for and runs I/O callbacks), check (setImmediate), and close callbacks. Between phases Node drains the process.nextTick queue and the promise microtask queue. Understanding phases explains why setImmediate runs before a 0 ms timer inside an I/O callback.
14

Is Node.js single-threaded? Explain the role of libuv.

Save to ▾
Show answer
Your JavaScript runs on a single main thread, but Node.js itself is not purely single-threaded. libuv, the C library under Node, provides the event loop and a thread pool (default 4 threads, set with UV_THREADPOOL_SIZE) used for file system work, DNS lookups, crypto and zlib. Network I/O uses OS async mechanisms like epoll or IOCP instead of threads.
15

What is the difference between process.nextTick and setImmediate?

Save to ▾
Show answer
process.nextTick queues a callback to run immediately after the current operation, before the event loop continues to any phase, even before promise microtasks. setImmediate queues a callback for the check phase of the next loop iteration. Overusing nextTick recursively can starve I/O because the loop never moves on, so setImmediate is safer for deferring heavy work.
16

How do you read a large file efficiently in Node.js?

Save to ▾
Show answer
const fs = require('fs'); const readline = require('readline'); async function countErrors(path) { const rl = readline.createInterface({ input: fs.createReadStream(path), crlfDelay: Infinity }); let count = 0; for await (const line of rl) { if (line.includes('ERROR')) count++; } return count; } // Streams line by line, so a 5 GB log uses little memory.
17

Your Node.js API becomes unresponsive when one endpoint generates a large PDF report. What is happening and how do you fix it?

Save to ▾
Show answer
The PDF generation is CPU-bound and blocks the single event loop, so every other request waits. Fixes: move the work to a worker_threads pool such as Piscina, or push it to a background job queue like BullMQ with Redis and return a job ID that the client polls. Also break large loops into chunks and measure event loop lag to confirm the fix.
18

How would you handle uncaughtException and unhandledRejection in production?

Save to ▾
Show answer
Log both with full stack traces and context, then exit the process after flushing logs, because state may be corrupted. A process manager like PM2, systemd or Kubernetes restarts it. Never use uncaughtException to keep running as if nothing happened. Since Node 15, unhandled rejections crash the process by default, so every promise chain should have proper error handling.
19

How do you implement graceful shutdown in a Node.js server?

Save to ▾
Show answer
const server = app.listen(3000); async function shutdown(signal) { console.log(signal, 'received, closing'); server.close(async () => { await db.close(); await redis.quit(); process.exit(0); }); setTimeout(() => process.exit(1), 10000).unref(); } process.on('SIGTERM', shutdown); process.on('SIGINT', shutdown); // Stops new connections, finishes in-flight requests, closes pools.
20

What is the purpose of the util.promisify function?

Save to ▾
Show answer
util.promisify converts a function that follows the Node callback convention, where the last argument is (err, result) => {}, into one that returns a promise. This lets you use async and await with older APIs. For example, const sleep = util.promisify(setTimeout) or promisify(fs.readFile). Many core modules now offer promise versions directly, such as fs/promises and dns/promises.
21

Describe a situation where you found and fixed a production performance problem.

Save to ▾
Show answer
Situation: our search API p95 latency jumped from 200 ms to 2 seconds after a release. Task: as on-call engineer I had to find the cause fast. Action: I checked APM traces, saw a new synchronous JSON transform blocking the event loop, moved it to a cached precomputed field and added an event loop lag alert. Result: latency returned to normal within a day and we added a performance check to code review.
22

What is backpressure in streams and how does pipeline handle it?

Save to ▾
Show answer
Backpressure happens when a readable source produces data faster than the writable destination can consume it, causing memory to grow. write() returns false when the internal buffer exceeds highWaterMark, and the producer should pause until the drain event. stream.pipeline and pipe handle this automatically, and pipeline also forwards errors and destroys all streams on failure, which pipe does not.
23

How do worker_threads differ from the cluster module?

Save to ▾
Show answer
worker_threads run JavaScript in parallel threads inside the same process and can share memory through SharedArrayBuffer; they suit CPU-heavy tasks like image processing or hashing. cluster forks multiple Node processes that share one server port, each with its own memory and event loop, to use all CPU cores for handling HTTP traffic. In containers, many teams prefer running more replicas instead of cluster.
24

Memory usage of a Node.js service grows steadily until the pod restarts. How would you find the leak?

Save to ▾
Show answer
First confirm with metrics that heap used keeps rising under steady traffic. Take heap snapshots with --inspect and Chrome DevTools or heapdump at intervals and compare them to find objects whose count keeps growing. Common causes are global caches without limits, event listeners added per request, closures holding large objects and unclosed timers. Fix, then load test with autocannon to verify heap stabilises.
25

You need to choose between Node.js and Java for a new high-traffic service. How would you decide?

Save to ▾
Show answer
I would look at the workload and team. Node.js fits I/O-heavy services like API gateways, chat, notifications and BFF layers, with fast development and shared JavaScript skills. Java fits CPU-heavy processing, complex multi-threaded logic and teams with strong JVM experience. I would benchmark a prototype for the real access pattern and consider hiring, libraries and existing platform tooling before deciding.
26

What is a callback and what is callback hell?

Save to ▾
Show answer
A callback is a function passed to another function to run when an asynchronous task finishes, following Node's error-first convention (err, data). Callback hell is deeply nested callbacks where each async step depends on the previous one, making code hard to read, handle errors in and maintain. Promises and async/await flatten the nesting and centralise error handling with catch or try/catch.
27

What is a Promise and what are its states?

Save to ▾
Show answer
A Promise is an object representing the eventual result of an asynchronous operation. It has three states: pending, fulfilled with a value, or rejected with a reason. Once settled it never changes. You consume it with then, catch and finally, or with await. Promises chain, so returning a value or promise from then passes it to the next step.
28

How do you run async tasks in parallel versus in sequence?

Save to ▾
Show answer
// Sequential: each waits for the previous one for (const id of ids) { const user = await getUser(id); results.push(user); } // Parallel: start all, then wait together const users = await Promise.all(ids.map((id) => getUser(id))); // Use sequential only when steps depend on each other // or when you must limit load on a downstream service.
29

Why is using async callbacks inside forEach a common bug?

Save to ▾
Show answer
Array.forEach ignores the promise returned by an async callback, so it does not wait for any iteration and errors are not caught by the outer function. Code after the loop runs before the work is done. Use for...of with await for sequential processing, or map the items to promises and await Promise.all for parallel processing.
30

Convert this callback-based function to async/await: fs.readFile(path, 'utf8', (err, data) => {...}).

Save to ▾
Show answer
const fs = require('fs/promises'); async function readConfig(path) { try { const data = await fs.readFile(path, 'utf8'); return JSON.parse(data); } catch (err) { if (err.code === 'ENOENT') return {}; throw err; } } // Or wrap any callback API with util.promisify(fn).
31

What is a closure and how is it used in Node.js code?

Save to ▾
Show answer
A closure is a function that remembers variables from the scope where it was created, even after that outer function has returned. In Node it is used for middleware factories like rateLimit({ max: 100 }), private counters, memoisation and module-level state. A risk is memory leaks when closures hold large objects longer than needed, for example inside long-lived event listeners.
32

What is the difference between var, let and const?

Save to ▾
Show answer
var is function-scoped, hoisted and initialised as undefined, and can be redeclared. let and const are block-scoped and are in a temporal dead zone until declared, so accessing them early throws an error. const prevents reassignment of the binding but not mutation of an object's contents. Modern Node code uses const by default and let when reassignment is needed.
33

How do you explain asynchronous programming to a non-technical stakeholder or junior teammate?

Save to ▾
Show answer
I use a restaurant analogy. One waiter takes an order, gives it to the kitchen and serves other tables instead of waiting at the stove; when the dish is ready, the kitchen rings a bell. Node.js works the same: it starts a database or network task, handles other requests and resumes when the result is ready. Then I show a short code example with async and await.
34

Explain the difference between Promise.all, Promise.allSettled, Promise.race and Promise.any.

Save to ▾
Show answer
Promise.all resolves with all results when every promise fulfils, and rejects as soon as one rejects. Promise.allSettled waits for all and returns each status and value or reason, useful when partial failure is fine. Promise.race settles with the first promise to settle, often used for timeouts. Promise.any resolves with the first fulfilled promise and rejects only if all reject.
35

How does async/await work under the hood?

Save to ▾
Show answer
An async function always returns a promise. await pauses that function until the awaited promise settles, then resumes it with the value or throws the rejection, while the event loop keeps running other work. Internally it is built on promises and generator-like suspension. The code reads like synchronous code but is still non-blocking, and errors are handled with ordinary try/catch.
36

What is the output order of this code: console.log('A'); setTimeout(() => console.log('B'), 0); Promise.resolve().then(() => console.log('C')); console.log('D');

Save to ▾
Show answer
The output is A, D, C, B. Synchronous code runs first, printing A and D. Then the microtask queue is drained before the event loop moves to timers, so the promise callback prints C. Finally the timers phase runs the setTimeout callback and prints B. Microtasks always run before the next macrotask such as timers or I/O callbacks.
37

What are microtasks and macrotasks?

Save to ▾
Show answer
Macrotasks are scheduled work like setTimeout, setInterval, setImmediate and I/O callbacks, handled phase by phase by the event loop. Microtasks are promise reactions and queueMicrotask callbacks; they run right after the current synchronous code finishes and before the next macrotask. In Node, process.nextTick callbacks run even before promise microtasks. A long microtask chain can delay timers and I/O.
38

How do you add a timeout to a promise?

Save to ▾
Show answer
function withTimeout(promise, ms) { let timer; const timeout = new Promise((_, reject) => { timer = setTimeout(() => reject(new Error('Timeout')), ms); }); return Promise.race([promise, timeout]).finally(() => clearTimeout(timer)); } await withTimeout(fetch(url), 3000); // For fetch, AbortSignal.timeout(3000) also cancels the request.
39

What happens if you forget to await a promise inside a try/catch?

Save to ▾
Show answer
If you call an async function without await, the try/catch only covers the synchronous part, so a later rejection escapes it and becomes an unhandled rejection that can crash the process. The function also continues before the work finishes, causing race conditions like responding before data is saved. ESLint rules such as no-floating-promises in TypeScript help catch this.
40

How does the this keyword behave in arrow functions compared to regular functions?

Save to ▾
Show answer
A regular function's this depends on how it is called: the object before the dot, undefined in strict mode for plain calls, or whatever bind, call or apply set. Arrow functions do not have their own this; they capture it lexically from the surrounding scope. That makes arrows ideal for callbacks inside class methods, but unsuitable as object methods that need this.
41

Write a retry function with exponential backoff for a flaky API call.

Save to ▾
Show answer
async function retry(fn, retries = 3, baseMs = 200) { for (let attempt = 0; ; attempt++) { try { return await fn(); } catch (err) { if (attempt >= retries) throw err; const delay = baseMs * 2 ** attempt + Math.random() * 100; await new Promise((r) => setTimeout(r, delay)); } } } await retry(() => paymentClient.getStatus(id));
42

A dashboard endpoint makes five independent database queries one after another and takes 1.5 seconds. How would you speed it up?

Save to ▾
Show answer
Because the queries are independent, run them concurrently with Promise.all so total time is close to the slowest query instead of the sum. Check that the database connection pool is large enough to serve them in parallel. Then add indexes for slow queries, cache rarely changing parts in Redis, and use Promise.allSettled if the page can render with some widgets missing.
43

How can you cancel an in-flight HTTP request in Node.js?

Save to ▾
Show answer
Use AbortController. Create a controller, pass controller.signal to fetch or to libraries that support it such as axios, and call controller.abort() when the request is no longer needed, for example when the client disconnects. AbortSignal.timeout(ms) gives a ready-made timeout signal. Aborting rejects the promise with an AbortError, which you should handle separately from real failures.
44

What is the difference between throwing an error and rejecting a promise inside an async function?

Save to ▾
Show answer
Inside an async function they behave the same: throw new Error() causes the returned promise to reject with that error. Outside async functions, a throw inside a plain callback such as setTimeout is not caught by promise handlers and becomes an uncaught exception. Always reject with Error objects, not strings, so you keep a stack trace.
45

A scheduled job processes orders with setInterval every minute, but sometimes runs overlap and duplicate emails are sent. How do you fix it?

Save to ▾
Show answer
setInterval fires regardless of whether the previous async run finished. Use a recursive setTimeout that schedules the next run only after the current one completes, or keep an isRunning flag. If several instances run, use a distributed lock in Redis or a job queue like BullMQ with unique job IDs. Also make email sending idempotent by recording sent message IDs.
46

Explain debounce and throttle and where they matter on the backend.

Save to ▾
Show answer
Debounce runs a function only after calls stop for a set time, merging bursts into one call. Throttle runs it at most once per interval. On the backend they help when batching cache invalidation, limiting webhook processing, writing metrics, or reacting to file watcher events. For API traffic you normally use a rate limiter instead, which is a server-side form of throttling.
47

Tell me about a time you had to debug a tricky asynchronous bug.

Save to ▾
Show answer
Situation: customers occasionally received two invoices. Task: I had to find why it happened only in production. Action: I added request IDs to logs and found a forEach with async callbacks plus a retry triggering a second save before the first finished. I replaced it with for...of, added a unique index on invoice number and made the job idempotent. Result: duplicates stopped completely.
48

How do you limit concurrency when calling an external API for 10,000 records?

Save to ▾
Show answer
async function runLimited(items, limit, worker) { const results = []; let i = 0; async function next() { while (i < items.length) { const idx = i++; results[idx] = await worker(items[idx]); } } await Promise.all(Array.from({ length: limit }, next)); return results; } await runLimited(records, 10, callPartnerApi); // Or use the p-limit package.
49

What are async iterators and when would you use for await...of?

Save to ▾
Show answer
An async iterator returns promises from its next() method, and for await...of consumes them one by one. In Node, readable streams, readline interfaces and many database cursors are async iterable. It is ideal for processing paginated API results or large query cursors item by item with natural backpressure, since the next chunk is not pulled until the current one is handled.
50

Two simultaneous requests both read a wallet balance, deduct money and save it, causing a wrong balance. How do you fix this race condition?

Save to ▾
Show answer
Async code interleaves between awaits, so read-modify-write is unsafe. Make the update atomic in the database: use an UPDATE with balance = balance - amount WHERE balance >= amount, or MongoDB $inc with a filter. Alternatives are row-level locks with SELECT FOR UPDATE inside a transaction, optimistic locking with a version column, or a Redis distributed lock for cross-service work.
51

Your service calls three microservices and fails completely if one is down. How would you make it more resilient?

Save to ▾
Show answer
Use Promise.allSettled so one failure does not reject everything, and return partial data with clear flags. Add timeouts to every call, retries with backoff for transient errors and a circuit breaker such as opossum to stop hammering a failing service. Provide fallbacks like cached values and log failures with correlation IDs so the issue can be traced.
52

What is Express.js and why is it widely used?

Save to ▾
Show answer
Express is a minimal, unopinionated web framework for Node.js that adds routing, middleware and helpers for requests and responses on top of the http module. It is widely used because it is simple, flexible and has a large ecosystem of middleware for logging, auth, validation and security. Many teams also use it as the base for frameworks like NestJS.
53

What is middleware in Express?

Save to ▾
Show answer
Middleware is a function with the signature (req, res, next) that runs during the request-response cycle. It can read or modify req and res, end the response, or call next() to pass control to the next middleware. Typical uses are parsing JSON, logging, authentication, CORS and rate limiting. Order matters, because middleware runs in the sequence it is registered.
54

What is the difference between app.use and app.get?

Save to ▾
Show answer
app.use mounts middleware for all HTTP methods and matches any path that starts with the given prefix, for example app.use('/api', router). app.get registers a handler only for GET requests on an exact route pattern. Use app.use for cross-cutting concerns like parsing and auth, and method-specific functions such as get, post, put and delete for endpoints.
55

How do you use express.Router to organise routes?

Save to ▾
Show answer
// routes/users.js const router = require('express').Router(); router.get('/', listUsers); router.get('/:id', getUser); router.post('/', auth, createUser); module.exports = router; // app.js app.use(express.json()); app.use('/api/v1/users', require('./routes/users')); // Keeps each resource in its own module with its own middleware.
56

What is the difference between req.params, req.query and req.body?

Save to ▾
Show answer
req.params holds route parameters from the path, such as id in /users/:id. req.query holds query string values like ?page=2&sort=name, used for filtering and pagination. req.body holds the parsed request payload of POST, PUT or PATCH and needs a parser such as express.json(). All are user input and must be validated before use.
57

How do you enable CORS in an Express app and why is it needed?

Save to ▾
Show answer
Browsers block cross-origin requests unless the server sends CORS headers. Use the cors package: app.use(cors({ origin: ['https://app.example.com'], credentials: true })). Avoid origin '*' with credentials, and restrict methods and headers you allow. CORS is a browser protection, not a security layer for your API, so you still need authentication and authorisation.
58

What does helmet do in an Express application?

Save to ▾
Show answer
helmet is middleware that sets secure HTTP response headers. It adds Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options nosniff, frame protection and removes X-Powered-By, reducing risks like clickjacking, MIME sniffing and some XSS attacks. It is one line, app.use(helmet()), but CSP usually needs tuning so your frontend scripts and assets still load.
59

What is the difference between res.send, res.json and res.end?

Save to ▾
Show answer
res.send sends a response and sets Content-Type based on the argument: string as HTML, Buffer as binary, object as JSON. res.json always serialises to JSON and sets application/json, and respects app settings like json spaces. res.end ends the response with no extra processing, useful for empty responses such as 204. Call only one per request to avoid headers-already-sent errors.
60

What causes the error 'Cannot set headers after they are sent to the client'?

Save to ▾
Show answer
It happens when code tries to send a second response for the same request, for example calling res.json in a callback and then again later, or forgetting return after sending an error response, or calling next() after responding. Fix it by returning immediately after each res.send or res.json, and making sure each code path sends exactly one response.
61

A client reports that a large JSON POST request fails with 413 Payload Too Large. What do you do?

Save to ▾
Show answer
express.json() has a default body limit of 100 KB, so bigger payloads are rejected with 413. If the size is legitimate, raise it for that route only, for example express.json({ limit: '2mb' }), and check any proxy limit such as Nginx client_max_body_size. For very large data, switch to file upload to object storage or pagination instead of huge JSON bodies.
62

Describe a time you disagreed with a senior developer on a technical approach.

Save to ▾
Show answer
Situation: a senior wanted to store sessions in server memory for a new API. Task: I believed it would break when we scaled to multiple pods. Action: I prepared a short comparison, showed a demo where users were logged out after a restart, and suggested Redis sessions or JWT. Result: we agreed on Redis, and the senior asked me to document the decision for the team.
63

How does error-handling middleware work in Express?

Save to ▾
Show answer
Error-handling middleware has four parameters (err, req, res, next) and must be registered after all routes. When any middleware calls next(err) or throws in a synchronous handler, Express skips normal middleware and jumps to the error handler. In Express 4, async errors must be passed to next manually or with a wrapper; Express 5 forwards rejected promises automatically.
64

Write a centralised error handler with a custom AppError class.

Save to ▾
Show answer
class AppError extends Error { constructor(message, status = 500) { super(message); this.status = status; } } app.use((err, req, res, next) => { const status = err.status || 500; logger.error({ err, path: req.path }); res.status(status).json({ error: status === 500 ? 'Internal server error' : err.message }); }); // In a route: throw new AppError('User not found', 404);
65

How do you handle async errors in Express 4 routes?

Save to ▾
Show answer
const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next); router.get('/:id', asyncHandler(async (req, res) => { const user = await User.findById(req.params.id); if (!user) throw new AppError('Not found', 404); res.json(user); })); // Without this, a rejected promise is never sent to the error handler. // Express 5 does this automatically.
66

Write a simple request logging middleware that records response time.

Save to ▾
Show answer
function requestLogger(req, res, next) { const start = process.hrtime.bigint(); res.on('finish', () => { const ms = Number(process.hrtime.bigint() - start) / 1e6; console.log(`${req.method} ${req.originalUrl} ${res.statusCode} ${ms.toFixed(1)}ms`); }); next(); } app.use(requestLogger); // In production use pino-http or morgan with structured logs.
67

How do you handle file uploads in Express?

Save to ▾
Show answer
Use multer for multipart/form-data. Configure limits for file size and count, filter allowed MIME types and extensions, and store files with generated names, not user-supplied names. For production, stream uploads directly to object storage such as S3 using presigned URLs so files do not pass through or fill the server disk. Scan files and never execute uploaded content.
68

How would you structure a large Express project?

Save to ▾
Show answer
I use a layered structure: routes define endpoints, controllers handle HTTP details, services hold business logic, and repositories or models talk to the database. Add middlewares, validators, config and utils folders, and group by feature, for example modules/orders, once the app grows. This keeps controllers thin, makes services unit-testable and avoids business rules leaking into routes.
69

How do you validate request bodies in Express?

Save to ▾
Show answer
const { z } = require('zod'); const createUser = z.object({ name: z.string().min(2), email: z.string().email(), age: z.number().int().min(18).optional() }); const validate = (schema) => (req, res, next) => { const r = schema.safeParse(req.body); if (!r.success) return res.status(400).json({ errors: r.error.issues }); req.body = r.data; next(); }; router.post('/users', validate(createUser), createUserHandler);
70

After adding a new middleware, all routes return 404 or hang. How do you debug it?

Save to ▾
Show answer
A hang usually means the middleware never calls next() or sends a response on some path. A 404 often means it was mounted with a wrong path or registered after routes. I check order of app.use calls, add logging at the start and end of the middleware, verify every branch calls next or responds, and check async code passes errors to next.
71

When would you choose NestJS or Fastify over Express?

Save to ▾
Show answer
Choose Fastify when raw performance and built-in schema validation matter; it is faster than Express thanks to optimised routing and serialisation. Choose NestJS for large teams that want structure: modules, dependency injection, decorators and first-class TypeScript, similar to Spring or Angular. Express still suits small to medium services and teams that prefer flexibility and a huge middleware ecosystem.
72

How do you serve a React build and an API from the same Express server?

Save to ▾
Show answer
const path = require('path'); app.use('/api', apiRouter); app.use(express.static(path.join(__dirname, 'client/dist'), { maxAge: '1y' })); // SPA fallback for client-side routes app.get(/^\/(?!api).*/, (req, res) => { res.sendFile(path.join(__dirname, 'client/dist/index.html')); }); // Register API routes first so they are not caught by the fallback.
73

How do you design Express middleware that checks user roles for a route?

Save to ▾
Show answer
Create a factory: const authorize = (...roles) => (req, res, next) => roles.includes(req.user?.role) ? next() : res.status(403).json({ error: 'Forbidden' }). Use it after authentication, for example router.delete('/:id', authenticate, authorize('admin'), handler). Keep roles and permissions in config or the database, and for complex rules move to permission-based checks or a policy library.
74

Tell me about a time you improved code quality in a backend codebase you inherited.

Save to ▾
Show answer
Situation: I joined a project where Express routes contained SQL, business logic and responses in 300-line handlers. Task: reduce bugs without stopping feature work. Action: I proposed a controller-service-repository structure, added an error handler, validation with zod and tests for each refactored route, and moved one module per sprint. Result: bug reports dropped and new developers onboarded faster.
75

What changed in Express 5 compared to Express 4?

Save to ▾
Show answer
Express 5, now the stable line, forwards rejected promises from async handlers to error middleware automatically. It updates path-to-regexp, so route patterns are stricter: wildcards must be named and some regex characters are no longer allowed in strings. Deprecated methods such as res.send(status) and req.param() were removed, and it requires a modern Node.js version. Upgrade by running tests and checking route patterns.
76

Your Express API needs to support multiple client apps with different rate limits. How would you design it?

Save to ▾
Show answer
Identify the client through an API key or OAuth client ID in authentication middleware and attach the plan to req. Then use express-rate-limit or a custom limiter with a Redis store, keyed by client ID, with limits read from the plan. Return 429 with Retry-After and RateLimit headers. Redis keeps limits consistent across multiple server instances.
77

What is a REST API and what are its key constraints?

Save to ▾
Show answer
REST is an architectural style where resources are identified by URLs and manipulated through standard HTTP methods. Key constraints: client-server separation, statelessness so each request carries all needed information, cacheable responses, uniform interface, layered system and optionally code on demand. In practice it means resource-based URLs, correct methods and status codes, and JSON representations.
78

Explain the main HTTP methods and which are idempotent.

Save to ▾
Show answer
GET reads a resource, POST creates or triggers an action, PUT replaces a resource fully, PATCH updates part of it and DELETE removes it. GET, PUT, DELETE, HEAD and OPTIONS are idempotent, meaning repeating the request has the same effect as doing it once. POST and usually PATCH are not, which is why payment APIs use idempotency keys for POST.
79

Which HTTP status codes should a REST API commonly return?

Save to ▾
Show answer
200 OK for successful reads and updates, 201 Created with a Location header for new resources, 204 No Content for deletes. 400 for invalid input, 401 when not authenticated, 403 when authenticated but not allowed, 404 not found, 409 conflict such as a duplicate email, 422 for validation errors in some APIs, 429 rate limited, 500 server error and 503 unavailable.
80

What is the difference between PUT and PATCH?

Save to ▾
Show answer
PUT replaces the entire resource with the representation sent, so missing fields may be reset; it is idempotent. PATCH applies a partial update, sending only changed fields, such as { "phone": "..." }. PATCH is more efficient for large objects. With JSON Merge Patch, repeated identical PATCH requests are effectively idempotent, but operations like increment are not.
81

How do you design good REST endpoint URLs?

Save to ▾
Show answer
Use plural nouns for resources and let methods express the action: GET /users, POST /users, GET /users/42, PATCH /users/42. Nest only for clear ownership, like GET /users/42/orders, and avoid deep nesting. Use lowercase kebab-case, query parameters for filtering, sorting and pagination, and version the API, for example /api/v1. Avoid verbs like /getUsers.
82

How do you document a REST API?

Save to ▾
Show answer
Use the OpenAPI 3 specification, written by hand in YAML or generated from code or validation schemas, and serve it with Swagger UI or Redoc. Document each endpoint's parameters, request and response schemas, status codes, auth and examples. Keep it in the repo, validate it in CI and generate client SDKs or contract tests from it so docs never drift from the code.
83

Compare offset pagination and cursor pagination.

Save to ▾
Show answer
Offset pagination uses page and limit, translated to LIMIT and OFFSET. It is simple and allows jumping to a page, but gets slow on large tables and can skip or repeat rows when data changes. Cursor pagination uses the last seen value, such as id or created_at, to fetch the next set with a WHERE clause. It is fast and stable, ideal for feeds and infinite scroll.
84

Implement cursor-based pagination for a products endpoint.

Save to ▾
Show answer
router.get('/products', async (req, res) => { const limit = Math.min(Number(req.query.limit) || 20, 100); const cursor = req.query.cursor; const filter = cursor ? { _id: { $lt: cursor } } : {}; const items = await Product.find(filter) .sort({ _id: -1 }) .limit(limit + 1); const hasMore = items.length > limit; if (hasMore) items.pop(); res.json({ data: items, nextCursor: hasMore ? items[items.length - 1]._id : null }); });
85

What are the common strategies for API versioning?

Save to ▾
Show answer
URL versioning like /api/v1/orders is the most common and easy to route and cache. Header versioning uses a custom header or Accept media type, keeping URLs clean but less visible. Query versioning like ?version=2 is simple but messy. Whatever you choose, version only for breaking changes, support old versions for a deprecation period and announce sunset dates.
86

How do you design a consistent error response format for an API?

Save to ▾
Show answer
Use one structure everywhere, for example { "error": { "code": "VALIDATION_FAILED", "message": "Email is invalid", "details": [{ "field": "email" }], "requestId": "abc123" } }. Many teams follow RFC 9457 Problem Details with type, title, status and detail. Use machine-readable codes for clients, human messages for display, and never leak stack traces or SQL errors.
87

What is the difference between REST and GraphQL?

Save to ▾
Show answer
REST exposes multiple endpoints with fixed response shapes, uses HTTP caching easily and is simple to reason about. GraphQL exposes one endpoint where clients query exactly the fields they need, reducing over-fetching and multiple round trips, which helps mobile apps. GraphQL adds complexity: N+1 query risks needing DataLoader, harder caching, query cost limits and schema governance.
88

How do you implement filtering and sorting safely from query parameters?

Save to ▾
Show answer
const ALLOWED_SORT = ['price', 'createdAt', 'name']; router.get('/products', async (req, res) => { const filter = {}; if (req.query.category) filter.category = String(req.query.category); if (req.query.maxPrice) filter.price = { $lte: Number(req.query.maxPrice) }; const field = ALLOWED_SORT.includes(req.query.sort) ? req.query.sort : 'createdAt'; const dir = req.query.order === 'asc' ? 1 : -1; res.json(await Product.find(filter).sort({ [field]: dir }).limit(50)); }); // Whitelist fields and cast types to block injection.
89

Design endpoints for a simple e-commerce cart and checkout.

Save to ▾
Show answer
GET /api/v1/cart returns the current user's cart. POST /api/v1/cart/items adds an item with productId and quantity. PATCH /api/v1/cart/items/:itemId changes quantity, and DELETE removes it. POST /api/v1/orders creates an order from the cart with an Idempotency-Key header. GET /api/v1/orders/:id shows status, and POST /api/v1/orders/:id/cancel handles cancellation as an action.
90

A mobile app team complains your API returns too much data and is slow on 4G. What would you change?

Save to ▾
Show answer
Add field selection like ?fields=id,name,price, smaller list representations with details on a separate endpoint, pagination with sensible limits and gzip or Brotli compression. Support ETag and Cache-Control so unchanged data returns 304. If many screens need combined data, consider a Backend-for-Frontend layer or GraphQL tailored to the mobile app.
91

You must change a response field name used by many clients. How do you do it without breaking them?

Save to ▾
Show answer
Treat it as a breaking change. Option one: add the new field alongside the old one, mark the old field deprecated in docs and response headers, track usage through logs and remove it after clients migrate. Option two: release it in /v2 and keep /v1 for a deprecation window. Communicate timelines clearly and add contract tests for both.
92

How does HTTP caching with ETag and Cache-Control work for APIs?

Save to ▾
Show answer
Cache-Control tells clients and CDNs how long a response is fresh, for example public, max-age=300, or private, no-store for user data. An ETag is a hash of the response; the client sends it back in If-None-Match, and if nothing changed the server returns 304 Not Modified with no body. Express generates weak ETags automatically for res.send responses.
93

How do you handle a breaking API change request from a product manager close to a release?

Save to ▾
Show answer
I would first understand why it is needed and who consumes the API. Then I explain the impact on existing clients, propose a non-breaking path such as adding a new field or a v2 endpoint, and estimate effort and risk. Result in a past case: we shipped the additive change on time and scheduled removal of the old field for the next quarter.
94

Tell me about an API you designed that you are proud of.

Save to ▾
Show answer
Situation: our support team used spreadsheets to track refunds. Task: I designed a refund API used by the admin panel and payment service. Action: I wrote the OpenAPI spec first, reviewed it with frontend and finance, added idempotency keys, clear error codes and pagination, and wrote contract tests. Result: integration took two days instead of a week, and refund errors dropped sharply.
95

What is idempotency and how do you implement idempotency keys for a payment API?

Save to ▾
Show answer
An idempotent operation gives the same result when repeated. For POST /payments, the client sends a unique Idempotency-Key header. The server stores the key with the request hash and response, for example in Redis or a table with a unique index. If the same key arrives again, it returns the saved response instead of charging twice. Keys expire after about 24 hours.
96

What is HATEOAS and is it used in real projects?

Save to ▾
Show answer
HATEOAS, Hypermedia as the Engine of Application State, means responses include links telling the client what it can do next, for example an order response with links to cancel or pay. It makes APIs more discoverable and decoupled. In practice most JSON APIs only partly use it, such as pagination links, because clients are usually built against documented endpoints.
97

How would you design an API for a long-running task such as exporting a 1 GB report?

Save to ▾
Show answer
Do not keep the HTTP request open. POST /exports creates a job, pushes it to a queue and returns 202 Accepted with a job ID and status URL. Clients poll GET /exports/:id or receive a webhook or WebSocket event. A worker generates the file, uploads it to object storage, and the status endpoint returns a short-lived signed download URL.
98

How do you implement and secure webhooks that your API sends to customers?

Save to ▾
Show answer
Send a POST with a JSON event, a unique event ID and timestamp. Sign the body with HMAC-SHA256 using a per-customer secret and include it in a header, so receivers verify authenticity and reject old timestamps to stop replays. Retry failures with exponential backoff, log delivery attempts and make events idempotent so receivers can safely handle duplicates.
99

Write code to verify an HMAC signature on an incoming webhook in Express.

Save to ▾
Show answer
const crypto = require('crypto'); app.post('/webhooks/payment', express.raw({ type: 'application/json' }), (req, res) => { const sig = req.get('X-Signature') || ''; const expected = crypto .createHmac('sha256', process.env.WEBHOOK_SECRET) .update(req.body) .digest('hex'); const ok = sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)); if (!ok) return res.status(401).end(); handleEvent(JSON.parse(req.body)); res.status(200).end(); });
100

A partner integration retries POST requests on timeouts and creates duplicate orders in your API. How would you redesign the endpoint?

Save to ▾
Show answer
Require an Idempotency-Key header on order creation and store it with a unique index alongside the created order ID and response. On a repeated key with the same payload, return the original response; with a different payload, return 409 or 422. Also add a unique business constraint such as partner order reference, respond quickly by queueing slow work, and document retry behaviour for partners.
101

What is the difference between authentication and authorisation?

Save to ▾
Show answer
Authentication verifies who the user is, for example by checking a password, OTP or OAuth login. Authorisation decides what that verified user is allowed to do, such as whether they can delete an order or view admin reports. In an API, authentication failures return 401 and authorisation failures return 403. Both must be enforced on the server, never only in the frontend.
102

What is a JWT and what are its three parts?

Save to ▾
Show answer
A JSON Web Token is a compact, signed token used to carry claims between parties. It has three Base64URL parts separated by dots: header (algorithm and type), payload (claims like sub, role, exp) and signature. The signature proves the token was issued by the server and not modified. The payload is only encoded, not encrypted, so never put secrets in it.
103

How should passwords be stored in a database?

Save to ▾
Show answer
Never store plain text or use fast hashes like MD5 or SHA-256 alone. Use a slow, salted password hashing algorithm such as bcrypt with a cost factor around 10 to 12, or Argon2id, which is the current recommendation. The library generates a unique salt per password. On login, compare using the library's compare function, which is timing-safe.
104

What is SQL injection and how do you prevent it in Node.js?

Save to ▾
Show answer
SQL injection happens when user input is concatenated into a query, letting attackers change it, for example ' OR 1=1 --. Prevent it with parameterised queries, such as pool.query('SELECT * FROM users WHERE email = $1', [email]) in pg, or an ORM like Prisma or Sequelize that parameterises automatically. Also validate input and give the database user minimum privileges.
105

How do you manage secrets such as database passwords and API keys in a Node.js app?

Save to ▾
Show answer
Never commit secrets to Git. Load them from environment variables, using a .env file only for local development and keeping it in .gitignore. In production, use a secrets manager such as AWS Secrets Manager, Azure Key Vault, HashiCorp Vault or Kubernetes secrets with encryption. Rotate keys regularly, scan repos with tools like gitleaks and validate required variables at startup.
106

How do you keep yourself updated about security best practices as a backend developer?

Save to ▾
Show answer
I follow the Node.js security release announcements and OWASP cheat sheets, read GitHub security advisories for packages we use and keep Dependabot alerts on. I also practise with labs like OWASP Juice Shop. In my last project I used this to propose helmet, rate limits and stricter input validation, and I shared a short security checklist that the team adopted in pull request reviews.
107

Compare session-based authentication with JWT-based authentication.

Save to ▾
Show answer
Sessions store state on the server, often in Redis, and the client holds only a session ID cookie; logout and revocation are easy. JWTs are stateless, so any service can verify them without a lookup, which suits microservices and mobile apps, but revoking a token before expiry needs a denylist or short lifetimes. Many systems combine short-lived JWT access tokens with server-stored refresh tokens.
108

How do access tokens and refresh tokens work together?

Save to ▾
Show answer
The access token is short-lived, around 5 to 15 minutes, and sent with each API request. The refresh token is long-lived, stored securely in an HttpOnly cookie, and used only at a refresh endpoint to get a new access token. Rotate refresh tokens on every use and store their hashes server-side, so a stolen or reused token can be detected and the session revoked.
109

Write JWT authentication middleware for Express.

Save to ▾
Show answer
const jwt = require('jsonwebtoken'); function authenticate(req, res, next) { const header = req.get('Authorization') || ''; const token = header.startsWith('Bearer ') ? header.slice(7) : null; if (!token) return res.status(401).json({ error: 'Missing token' }); try { req.user = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] }); next(); } catch { res.status(401).json({ error: 'Invalid or expired token' }); } }
110

Write signup and login handlers using bcrypt.

Save to ▾
Show answer
const bcrypt = require('bcrypt'); async function signup(req, res) { const hash = await bcrypt.hash(req.body.password, 12); const user = await User.create({ email: req.body.email, password: hash }); res.status(201).json({ id: user.id }); } async function login(req, res) { const user = await User.findOne({ email: req.body.email }).select('+password'); const ok = user && await bcrypt.compare(req.body.password, user.password); if (!ok) return res.status(401).json({ error: 'Invalid credentials' }); res.json({ token: signAccessToken(user) }); }
111

Where should a web app store tokens: localStorage or cookies?

Save to ▾
Show answer
localStorage is readable by any JavaScript on the page, so an XSS bug can steal tokens. HttpOnly, Secure, SameSite cookies cannot be read by JavaScript, which is safer for refresh tokens and session IDs. Cookies require CSRF protection, which SameSite=Lax or Strict plus CSRF tokens provide. A common pattern keeps the access token in memory and the refresh token in an HttpOnly cookie.
112

What is the difference between OAuth 2.0 and OpenID Connect?

Save to ▾
Show answer
OAuth 2.0 is an authorisation framework that issues access tokens for calling APIs; it does not define who the user is. OpenID Connect is an identity layer on top of OAuth 2.0 that adds an ID token, a JWT with user identity claims, and a userinfo endpoint. For social or enterprise single sign-on you use OIDC; for API access delegation you use OAuth scopes.
113

What is NoSQL injection in MongoDB and how do you prevent it?

Save to ▾
Show answer
If you pass req.body directly into a query, an attacker can send { "password": { "$ne": null } } and bypass checks. Prevent it by validating and casting input types with zod or Joi, rejecting objects where strings are expected, enabling Mongoose strictQuery and sanitizeFilter, and never building queries from raw user objects. Avoid $where and server-side JavaScript evaluation.
114

What are XSS and CSRF and how do you protect a Node.js app from them?

Save to ▾
Show answer
XSS injects malicious script into pages other users view; prevent it by escaping output, using templating or React auto-escaping, sanitising rich HTML with DOMPurify and setting a strong Content-Security-Policy. CSRF tricks a logged-in browser into sending unwanted requests; prevent it with SameSite cookies, CSRF tokens for cookie-based auth and checking Origin headers on state-changing requests.
115

How do you implement rate limiting on a login endpoint?

Save to ▾
Show answer
const rateLimit = require('express-rate-limit'); const { RedisStore } = require('rate-limit-redis'); const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, limit: 5, standardHeaders: 'draft-7', legacyHeaders: false, keyGenerator: (req) => `${req.ip}:${req.body.email}`, store: new RedisStore({ sendCommand: (...a) => redis.sendCommand(a) }) }); app.post('/auth/login', loginLimiter, login); // Add account lockout or CAPTCHA after repeated failures.
116

How do you implement role-based access control (RBAC) in an API?

Save to ▾
Show answer
Define roles such as admin, manager and customer, and map them to permissions like order:read or order:refund. Put the role or permissions in the token or load them per request, and check them in middleware before handlers. For ownership rules, also check resource data, for example a customer can view only orders where order.userId equals req.user.id. Test both allowed and denied paths.
117

What is the OWASP Top 10 and which items matter most for Node.js APIs?

Save to ▾
Show answer
The OWASP Top 10 lists the most critical web application security risks. For Node APIs the most relevant are broken access control, injection, cryptographic failures, security misconfiguration, vulnerable and outdated components, authentication failures and server-side request forgery. OWASP also publishes an API Security Top 10, which highlights broken object level authorisation, where users access others' data by changing an ID.
118

A security audit finds that users can see other users' orders by changing the order ID in the URL. How do you fix it?

Save to ▾
Show answer
This is broken object level authorisation. Every query must be scoped to the authenticated user: Order.findOne({ _id: id, userId: req.user.id }), returning 404 if not found. Put this check in a shared service or middleware so no route can skip it, add tests that try other users' IDs, and consider non-guessable IDs like UUIDs as defence in depth, not as the fix.
119

How would you add OTP-based login for an Indian mobile app?

Save to ▾
Show answer
Generate a random 6-digit OTP with crypto.randomInt, store only its hash in Redis with a 5-minute TTL and an attempt counter, and send it through an SMS provider on a DLT-registered template. Verify with a timing-safe compare, limit to about 3 to 5 attempts and rate limit sends per number and IP. On success, issue access and refresh tokens and delete the OTP.
120

npm audit reports a critical vulnerability in a dependency of a dependency. What do you do?

Save to ▾
Show answer
First check whether the vulnerable code path is actually reachable in our usage and read the advisory. Try npm audit fix or updating the parent package to a version that pulls a patched sub-dependency. If none exists, use the overrides field in package.json to force a safe version and run tests. Track it in a ticket and enable Dependabot or Renovate for ongoing updates.
121

Tell me about a time you found a security issue in code and how you handled it.

Save to ▾
Show answer
Situation: during code review I noticed an admin export route that only hid the button in the frontend. Task: make sure no regular user could call it. Action: I reproduced it with a normal user token, reported it privately to my lead, added role middleware and tests, and checked similar routes. Result: we fixed four endpoints before release and added an authorisation checklist to pull requests.
122

Explain OAuth 2.0 and the Authorization Code flow with PKCE.

Save to ▾
Show answer
OAuth 2.0 lets an app access resources on a user's behalf without seeing their password, for example social login with an existing account. In Authorization Code with PKCE, the app creates a code verifier and sends its hash to the authorisation server. The user logs in and consents, the app receives a code and exchanges it plus the verifier for tokens. PKCE stops intercepted codes being reused.
123

Your JWT secret has leaked. What steps do you take?

Save to ▾
Show answer
Treat it as an incident. Generate a new secret and deploy it immediately, which invalidates all existing tokens and forces re-login. Revoke refresh tokens in the database, check logs for suspicious tokens or admin actions, and find how it leaked, for example a committed .env or logs. Move to a secrets manager, consider asymmetric RS256 keys with key IDs to rotate safely, and document the incident.
124

How would you design authentication for a system of ten microservices?

Save to ▾
Show answer
Centralise login in an identity service or provider such as Keycloak, Auth0 or Cognito that issues short-lived JWTs signed with RS256. The API gateway validates tokens, and each service verifies the signature using the public keys from a JWKS endpoint, so no shared secret is needed. Use service-to-service tokens or mTLS for internal calls and keep authorisation checks inside each service.
125

How do you prevent server-side request forgery when your API fetches a user-supplied URL?

Save to ▾
Show answer
An attacker could make your server call internal addresses such as 169.254.169.254 for cloud metadata or localhost admin ports. Allow only http and https, resolve the hostname and block private, loopback and link-local IP ranges, use an allowlist of domains where possible, disable redirects or revalidate them, set short timeouts and size limits, and run fetchers in an isolated network.
126

What is the difference between SQL and NoSQL databases?

Save to ▾
Show answer
SQL databases such as PostgreSQL and MySQL store data in tables with fixed schemas, support joins and strong ACID transactions, and suit relational data like orders and payments. NoSQL databases such as MongoDB, Redis or Cassandra use documents, key-value or wide-column models with flexible schemas and easier horizontal scaling. The choice depends on data relationships, consistency needs and query patterns.
127

What is Mongoose and why use it with MongoDB?

Save to ▾
Show answer
Mongoose is an ODM for MongoDB in Node.js. It adds schemas with types, validation, defaults, middleware hooks such as pre save, virtuals, population of references and a convenient query API. This brings structure to schemaless MongoDB collections and reduces bugs. For very high-performance or simple cases, some teams use the native MongoDB driver directly to avoid the overhead.
128

Define a Mongoose schema for a user with validation and an index.

Save to ▾
Show answer
const { Schema, model } = require('mongoose'); const userSchema = new Schema({ name: { type: String, required: true, trim: true }, email: { type: String, required: true, unique: true, lowercase: true }, password: { type: String, required: true, select: false }, role: { type: String, enum: ['user', 'admin'], default: 'user' }, city: String }, { timestamps: true }); userSchema.index({ city: 1, createdAt: -1 }); module.exports = model('User', userSchema);
129

What is an index and how does it improve query performance?

Save to ▾
Show answer
An index is a data structure, usually a B-tree, that lets the database find rows without scanning the whole table or collection. Indexes speed up reads on filtered, sorted and joined fields but slow down writes and use storage. Create them based on real query patterns, check usage with EXPLAIN or explain('executionStats'), and remove unused ones.
130

Explain ACID properties of a database transaction.

Save to ▾
Show answer
Atomicity means all operations in a transaction succeed or none do. Consistency means the database moves from one valid state to another, respecting constraints. Isolation means concurrent transactions do not see each other's partial work, controlled by isolation levels. Durability means committed data survives crashes. Money transfers rely on ACID so a debit never happens without the matching credit.
131

Write a parameterised query with node-postgres to fetch a user's recent orders.

Save to ▾
Show answer
const { Pool } = require('pg'); const pool = new Pool({ connectionString: process.env.DATABASE_URL, max: 10 }); async function recentOrders(userId, limit = 10) { const { rows } = await pool.query( `SELECT id, amount, status, created_at FROM orders WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2`, [userId, limit] ); return rows; }
132

What is Redis and how is it used with Node.js backends?

Save to ▾
Show answer
Redis is an in-memory key-value store with data types like strings, hashes, lists, sets, sorted sets and streams. Node apps use it for caching database results, session storage, rate limiting counters, distributed locks, leaderboards, pub/sub between instances and job queues through BullMQ. Data lives in memory, so use TTLs and eviction policies and do not treat it as the only copy of important data.
133

What is a compound index and why does field order matter?

Save to ▾
Show answer
A compound index covers multiple fields, such as { userId: 1, createdAt: -1 }. It supports queries that use a prefix of the fields, so it helps queries on userId alone or userId plus createdAt, but not createdAt alone. A good rule in MongoDB is ESR: put equality fields first, then sort fields, then range fields, to avoid in-memory sorts.
134

What is the N+1 query problem and how do you fix it?

Save to ▾
Show answer
N+1 happens when you run one query to fetch a list and then one extra query per item, for example loading 100 posts and then each author separately, causing 101 queries. Fix it with joins or eager loading in SQL ORMs like include in Prisma or Sequelize, populate or $lookup in MongoDB, batching with an IN query, or DataLoader in GraphQL.
135

What is the MongoDB aggregation pipeline? Give an example.

Save to ▾
Show answer
// Top 5 cities by revenue in the last 30 days const since = new Date(Date.now() - 30 * 864e5); const result = await Order.aggregate([ { $match: { status: 'paid', createdAt: { $gte: since } } }, { $group: { _id: '$city', revenue: { $sum: '$amount' }, orders: { $sum: 1 } } }, { $sort: { revenue: -1 } }, { $limit: 5 } ]); // Each stage transforms documents; put $match early to use indexes.
136

When should you embed documents versus reference them in MongoDB?

Save to ▾
Show answer
Embed when data is read together, owned by the parent and bounded in size, such as addresses inside a user or line items inside an order. Reference when the related data is large, grows without limit, is shared across many documents or changes independently, such as products referenced by orders or comments on a viral post. Remember the 16 MB document size limit.
137

What is connection pooling and why is it important in Node.js?

Save to ▾
Show answer
Opening a database connection is slow, so a pool keeps a set of open connections that requests borrow and return. pg Pool, mysql2 and the MongoDB driver all pool by default. Size the pool based on database limits and instance count; too small causes waiting, too large overloads the database. In serverless functions, reuse the client across invocations or use a proxy like RDS Proxy.
138

Compare Prisma, Sequelize and TypeORM for SQL databases in Node.js.

Save to ▾
Show answer
Prisma uses a schema file to generate a fully typed client and has excellent TypeScript support and migrations, though complex raw SQL needs escape hatches. Sequelize is mature, promise-based and widely used in older JavaScript projects. TypeORM uses decorators and entity classes, familiar to Java and NestJS developers. Drizzle is a newer lightweight, SQL-like typed option. Pick based on TypeScript use and team familiarity.
139

Implement the cache-aside pattern with Redis for a product lookup.

Save to ▾
Show answer
async function getProduct(id) { const key = `product:${id}`; const cached = await redis.get(key); if (cached) return JSON.parse(cached); const product = await Product.findById(id).lean(); if (product) { await redis.set(key, JSON.stringify(product), { EX: 300 }); } return product; } async function updateProduct(id, data) { await Product.updateOne({ _id: id }, data); await redis.del(`product:${id}`); // invalidate }
140

What is database normalisation and when would you denormalise?

Save to ▾
Show answer
Normalisation organises tables to remove duplicate data and update anomalies, typically up to third normal form, for example keeping customers and orders in separate tables linked by foreign keys. You denormalise for read performance, such as storing a product name on an order line or a precomputed order count on a user, when joins become expensive. Denormalised data must be kept in sync.
141

How do you manage database schema migrations in a Node.js project?

Save to ▾
Show answer
Use a migration tool such as Prisma Migrate, Knex, Sequelize CLI or TypeORM migrations, and commit migration files to Git. Each migration has up and ideally down steps and runs in CI and before deployment. For zero downtime, use expand and contract: add new columns first, deploy code that writes both, backfill, switch reads, then drop old columns in a later release.
142

Users report seeing stale prices after an update because of caching. How do you solve it?

Save to ▾
Show answer
Invalidate or update the cache key in the same code path that updates the price, preferably after the database commit. Use reasonable TTLs as a safety net, and version keys if needed. If several services update prices, publish a price-changed event and have consumers delete the cache. For CDN-cached responses, purge the relevant URLs or use short max-age with stale-while-revalidate.
143

A Mongoose query returns large documents and the API is slow. What optimisations can you apply?

Save to ▾
Show answer
Use .select() to fetch only needed fields, and .lean() to return plain objects instead of full Mongoose documents, which cuts memory and CPU. Make sure the filter and sort use an index, checked with explain. Paginate results, avoid populating deep references, and cache frequently read data. For large exports, use a cursor with for await instead of loading everything.
144

What is soft delete and what are its trade-offs?

Save to ▾
Show answer
Soft delete marks a record as deleted with a flag or deletedAt timestamp instead of removing it, so data can be restored and audited. Trade-offs: every query must filter out deleted rows, unique constraints need care, tables grow over time and privacy laws such as India's DPDP Act may require real deletion of personal data on request. Use partial indexes and periodic purging.
145

Tell me about a time a database change caused an issue and what you learned.

Save to ▾
Show answer
Situation: I added a NOT NULL column with a default to a large MySQL table during business hours. Task: the migration locked the table and checkout slowed down. Action: I rolled back, rewrote it as add nullable column, backfill in batches, then add the constraint, and ran it at low traffic after testing on a production-size copy. Result: no downtime, and we added a migration review checklist.
146

How do you use a MongoDB transaction in Mongoose?

Save to ▾
Show answer
const session = await mongoose.startSession(); try { await session.withTransaction(async () => { await Account.updateOne( { _id: fromId, balance: { $gte: amount } }, { $inc: { balance: -amount } }, { session } ); await Account.updateOne({ _id: toId }, { $inc: { balance: amount } }, { session }); await Transfer.create([{ fromId, toId, amount }], { session }); }); } finally { await session.endSession(); } // Requires a replica set or Atlas cluster.
147

An API listing orders became slow after the table reached 50 million rows. How do you investigate?

Save to ▾
Show answer
Run EXPLAIN ANALYZE on the slow query to see whether it does a sequential scan or a costly sort. Add a composite index matching the filter and sort, for example (user_id, created_at DESC). Replace OFFSET pagination with cursor pagination, select only needed columns, and consider partitioning by date and archiving old rows. Monitor slow query logs after the change.
148

Your app gets 'too many connections' errors from PostgreSQL after scaling to 20 pods. What would you do?

Save to ▾
Show answer
Each pod opens its own pool, so 20 pods with max 20 connections need 400, beyond the database limit. Reduce the per-pod pool size, put PgBouncer or RDS Proxy in front of the database for transaction pooling, and make sure connections are released, for example client.release() in finally blocks. Also check for long-running idle transactions holding connections.
149

You need to choose a database for a food delivery app's orders, live rider locations and menus. What would you pick?

Save to ▾
Show answer
Orders and payments need strong consistency and transactions, so PostgreSQL or MySQL fits. Live rider locations change every few seconds and need geo queries, so Redis with geospatial commands works well for the hot data, with history stored elsewhere. Menus are read-heavy and nested, so a document store like MongoDB or Postgres JSONB plus caching suits them.
150

What is the difference between unit, integration and end-to-end tests for a Node.js API?

Save to ▾
Show answer
Unit tests check a single function or service in isolation with dependencies mocked; they are fast and numerous. Integration tests check several parts together, such as a route, service and a real test database. End-to-end tests run the full system like a user would, often through HTTP against a deployed environment. A healthy test pyramid has many unit tests and fewer E2E tests.
151

Write a Jest unit test for a function that calculates order total with GST.

Save to ▾
Show answer
// pricing.js const total = (items, gst = 0.18) => Math.round(items.reduce((s, i) => s + i.price * i.qty, 0) * (1 + gst) * 100) / 100; module.exports = { total }; // pricing.test.js const { total } = require('./pricing'); describe('total', () => { it('adds 18% GST by default', () => { expect(total([{ price: 100, qty: 2 }])).toBe(236); }); it('returns 0 for an empty cart', () => { expect(total([])).toBe(0); }); });
152

How do you debug a Node.js application?

Save to ▾
Show answer
Start Node with --inspect or --inspect-brk and attach Chrome DevTools at chrome://inspect or the VS Code debugger to set breakpoints, step through code and inspect variables. Use structured logs with request IDs to trace issues in deployed environments. For async issues, check stack traces with async context, and for performance use --cpu-prof or clinic.js flame graphs.
153

Why is structured logging important and which libraries are used?

Save to ▾
Show answer
Structured logs are JSON lines with fields like level, timestamp, requestId, userId and message, so tools like ELK, Loki, Datadog or CloudWatch can search and aggregate them. pino is fast and popular, winston is flexible with many transports. Log at correct levels, include a correlation ID per request, and never log passwords, tokens, full card numbers or Aadhaar numbers.
154

What is code coverage and is 100 percent coverage a good goal?

Save to ▾
Show answer
Code coverage measures how much code tests execute: lines, branches, functions and statements, reported by Jest with --coverage or c8. It shows untested areas but not whether assertions are meaningful. Chasing 100 percent leads to brittle tests of trivial code. A practical target is around 70 to 85 percent with strong coverage of business logic, error paths and security checks.
155

A new developer pushes code that breaks the login API in staging. How would you prevent this in future?

Save to ▾
Show answer
Add automated checks before merge: unit and integration tests for login in the CI pipeline, branch protection requiring green builds and at least one review, linting and type checks. Add a smoke test that runs after each staging deploy and alerts on failure. Treat it as a blameless learning moment and pair with the developer to write the missing tests.
156

How do you handle a situation where you cannot find the root cause of a bug before a deadline?

Save to ▾
Show answer
I inform my lead early with what I know, what I have ruled out and the risk. I propose a safe mitigation, such as a feature flag to disable the path or a rollback, and add extra logging to capture more data. In one case this let us release on time with the feature off, and the new logs revealed the cause two days later.
157

How do you test Express routes with Supertest?

Save to ▾
Show answer
const request = require('supertest'); const app = require('../app'); // export app without calling listen describe('GET /api/v1/users/:id', () => { it('returns 404 for an unknown user', async () => { const res = await request(app) .get('/api/v1/users/000000000000000000000000') .set('Authorization', `Bearer ${testToken}`); expect(res.status).toBe(404); expect(res.body.error).toBeDefined(); }); });
158

How do you mock a database or external service in Jest?

Save to ▾
Show answer
// order.service.test.js jest.mock('../repositories/orderRepo'); const repo = require('../repositories/orderRepo'); const { cancelOrder } = require('../services/order.service'); test('cannot cancel a shipped order', async () => { repo.findById.mockResolvedValue({ id: 1, status: 'shipped' }); await expect(cancelOrder(1)).rejects.toThrow('Cannot cancel'); expect(repo.update).not.toHaveBeenCalled(); }); // For HTTP calls, nock or msw can intercept requests.
159

What is the built-in node:test runner and when would you use it over Jest?

Save to ▾
Show answer
Node.js ships a stable test runner in the node:test module with describe, it, mocks, coverage and watch mode, run with node --test. It needs no extra dependency and works natively with ES modules, which Jest still handles with extra configuration. Jest or Vitest offer richer snapshot testing, mocking and ecosystem. For small libraries or ESM-first services, node:test is a lightweight choice.
160

How do you run integration tests against a real database in CI?

Save to ▾
Show answer
Start a disposable database for the test run, using Docker Compose services in CI, Testcontainers from the test code, or mongodb-memory-server for MongoDB. Run migrations before tests, seed minimal data per test, and clean tables between tests or wrap each test in a rolled-back transaction. Use a separate DATABASE_URL so tests can never touch production data.
161

A test passes locally but fails randomly in CI. How do you approach this flaky test?

Save to ▾
Show answer
Run it repeatedly to reproduce, then look for shared state between tests, missing awaits, reliance on test order, real timers, current date or time zones, random data and network calls. Fix by isolating data per test, using fake timers, freezing dates, mocking external calls and awaiting all promises. Quarantine the test only temporarily with a ticket, never delete it silently.
162

How would you test code that depends on the current time, like an expiring coupon?

Save to ▾
Show answer
Inject a clock function instead of calling Date.now directly, or use Jest fake timers: jest.useFakeTimers().setSystemTime(new Date('2026-01-01')). Then test before, exactly at and after expiry. Store dates in UTC and convert for display in IST, which avoids bugs near midnight. Reset timers after each test with jest.useRealTimers to avoid leaking into other tests.
163

Tell me about a production incident you handled and what you did after it.

Save to ▾
Show answer
Situation: our payment callback endpoint started failing after a deploy on a Friday evening. Task: I was on call. Action: I checked error logs, saw a missing environment variable, rolled back within ten minutes, then added startup config validation and a post-deploy smoke test. Result: impact was limited to a few failed callbacks we replayed, and I wrote a blameless postmortem the team used to improve deploy checks.
164

Describe how you would mentor a junior developer who writes no tests.

Save to ▾
Show answer
I would start by understanding why: lack of time, knowledge or examples. Then I pair with them on one feature, writing a test first for a real bug so they see the value. I share a small template for service and route tests, review their tests kindly in pull requests and celebrate when a test catches a regression. Over a few sprints, testing becomes a habit.
165

How do you add a correlation ID to every log line for a request?

Save to ▾
Show answer
const { AsyncLocalStorage } = require('async_hooks'); const { randomUUID } = require('crypto'); const als = new AsyncLocalStorage(); app.use((req, res, next) => { const id = req.get('X-Request-Id') || randomUUID(); res.set('X-Request-Id', id); als.run({ requestId: id }, next); }); const log = (msg, extra = {}) => logger.info({ ...extra, requestId: als.getStore()?.requestId }, msg); // Pass the ID to downstream services in headers.
166

Production shows intermittent 502 errors from the load balancer but your app logs show nothing. How do you investigate?

Save to ▾
Show answer
A 502 means the load balancer got a bad or no response from the app. Check whether pods restarted or crashed through OOM kills, health check failures and deployment times. A common Node cause is keep-alive mismatch: set server.keepAliveTimeout higher than the load balancer idle timeout and headersTimeout above that. Also check event loop blocking and connection limits.
167

How do you find which function is using the most CPU in a Node.js service?

Save to ▾
Show answer
Reproduce the load with autocannon or k6, then profile with node --cpu-prof to generate a .cpuprofile file and open it in Chrome DevTools, or use clinic flame to get a flame graph. Wide bars show hot functions. Common culprits are JSON.stringify on huge objects, synchronous crypto, regex backtracking and heavy loops. Fix and re-measure under the same load.
168

What is contract testing and why is it useful for microservices?

Save to ▾
Show answer
Contract testing verifies that a provider API and its consumers agree on request and response formats without running the whole system. With Pact, the consumer writes expectations that become a contract, and the provider runs them in its own CI. It catches breaking changes like a renamed field before deployment, and is faster and less flaky than full end-to-end environments.
169

How do you load test a Node.js API before a big sale event?

Save to ▾
Show answer
Define targets from expected traffic, such as 5,000 requests per second with p95 under 300 ms. Write realistic scenarios in k6 or Artillery covering browse, cart and checkout, run them against a production-like environment, and ramp up gradually. Watch latency, error rates, CPU, memory, event loop lag and database metrics, then fix bottlenecks and rerun until targets are met with headroom.
170

A regex in your validation code makes the server hang on certain inputs. What is the issue?

Save to ▾
Show answer
This is likely ReDoS, regular expression denial of service. Patterns with nested quantifiers such as (a+)+ can backtrack exponentially on crafted input, blocking the event loop. Fix by simplifying the regex, limiting input length before matching, using well-tested validators like validator.js, or a linear-time engine such as re2. Add tests with long malicious strings.
171

After a Node.js version upgrade, a few integration tests fail with date and crypto differences. How do you handle the upgrade safely?

Save to ▾
Show answer
Read the release notes and changelog for breaking changes, such as deprecated APIs, OpenSSL updates or changed default behaviour. Run the full test suite on the new version in a CI matrix alongside the old one, fix failures one by one and check native modules rebuild correctly. Roll out to staging, then to a canary in production while watching error rates before upgrading all services.
172

What is PM2 and when do you need it?

Save to ▾
Show answer
PM2 is a process manager for Node.js on virtual machines. It keeps apps running and restarts them on crashes, runs cluster mode across CPU cores, supports zero-downtime reloads, log management and startup scripts. In Docker or Kubernetes you usually do not need PM2, because the orchestrator restarts containers and you scale with replicas, so you run node directly.
173

Why put Nginx in front of a Node.js application?

Save to ▾
Show answer
Nginx acts as a reverse proxy: it terminates TLS, serves static files efficiently, compresses responses, buffers slow clients, load balances across multiple Node instances and adds rate limiting and request size limits. This frees Node to focus on application logic. On cloud platforms a managed load balancer or ingress controller often plays the same role.
174

What are common ways to deploy a Node.js API in 2026?

Save to ▾
Show answer
Options include virtual machines like EC2 with PM2 and Nginx, containers on Kubernetes such as EKS, AKS or GKE, managed container services like AWS ECS Fargate, Cloud Run or Azure Container Apps, platforms like Render or Railway, and serverless functions on AWS Lambda. Choice depends on traffic patterns, team skills, cost and need for control.
175

Write a simple Docker Compose file to run a Node API with MongoDB and Redis locally.

Save to ▾
Show answer
services: api: build: . ports: ['3000:3000'] environment: MONGO_URL: mongodb://mongo:27017/app REDIS_URL: redis://redis:6379 depends_on: [mongo, redis] mongo: image: mongo:7 volumes: ['mongo-data:/data/db'] redis: image: redis:7-alpine volumes: mongo-data:
176

How do you manage configuration for dev, staging and production environments?

Save to ▾
Show answer
Follow the twelve-factor approach: keep config in environment variables, not code. Load and validate them at startup with a schema, for example zod or envalid, and fail fast if any are missing. Use .env files only locally, and store production values in the platform's secret store or config maps. Keep the same Docker image across environments and change only configuration.
177

Why do you want to work as a Node.js backend developer?

Save to ▾
Show answer
I enjoy building the systems behind products: APIs, data models and integrations. Node.js lets me use JavaScript across the stack and ship features quickly, and I like solving problems around performance and reliability. In my projects I built a REST API with authentication, MongoDB and Docker deployment, and I want to grow by working on real traffic and learning system design from experienced engineers.
178

Write a Dockerfile for a production Node.js API.

Save to ▾
Show answer
FROM node:22-alpine AS deps WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev FROM node:22-alpine ENV NODE_ENV=production WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . USER node EXPOSE 3000 HEALTHCHECK CMD wget -qO- http://localhost:3000/health || exit 1 CMD ["node", "src/server.js"]
179

How do you scale a Node.js application horizontally?

Save to ▾
Show answer
Run multiple stateless instances behind a load balancer and add more as load grows, using autoscaling on CPU, memory or request rate. Keep state out of the process: sessions in Redis, files in object storage, jobs in a queue. Use Redis adapters for WebSocket broadcasting across instances, and make sure the database can handle the total connection count.
180

Write a basic GitHub Actions workflow that tests and builds a Node.js app.

Save to ▾
Show answer
name: ci on: [push, pull_request] jobs: test: runs-on: ubuntu-latest services: mongo: image: mongo:7 ports: ['27017:27017'] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - run: npm run lint - run: npm test -- --coverage env: MONGO_URL: mongodb://localhost:27017/test
181

What are health check endpoints and what is the difference between liveness and readiness?

Save to ▾
Show answer
Health endpoints let orchestrators check the app. Liveness answers whether the process is alive; if it fails, the container is restarted, so keep it simple. Readiness answers whether the instance can serve traffic, checking database and cache connections; if it fails, the load balancer stops sending requests but does not restart it. During shutdown, fail readiness first.
182

How do you use a message queue to process background jobs in Node.js?

Save to ▾
Show answer
const { Queue, Worker } = require('bullmq'); const connection = { host: 'redis', port: 6379 }; const emailQueue = new Queue('email', { connection }); await emailQueue.add('welcome', { userId }, { attempts: 5, backoff: { type: 'exponential', delay: 1000 }, jobId: `welcome:${userId}` }); new Worker('email', async (job) => { await sendWelcomeEmail(job.data.userId); }, { connection, concurrency: 10 });
183

How do you implement real-time features like live order tracking in Node.js?

Save to ▾
Show answer
Use WebSockets through Socket.IO or ws, or Server-Sent Events for one-way updates. Clients join a room per order, and when the rider location or status changes, the server emits to that room. With multiple instances, use the Socket.IO Redis adapter so events reach clients on any server, authenticate the socket on connect, and throttle location updates to save bandwidth.
184

What metrics would you monitor for a Node.js API in production?

Save to ▾
Show answer
Use the RED method for requests: rate, errors and duration with p50, p95 and p99. Add Node-specific metrics: event loop lag, heap used, garbage collection time and active handles, plus CPU, memory and restarts. Monitor dependencies such as database query time, pool usage and cache hit ratio. Export with prom-client or OpenTelemetry and alert on symptoms users feel.
185

Should you use serverless functions like AWS Lambda for a Node.js API?

Save to ▾
Show answer
Serverless suits spiky or low-traffic workloads, event processing and quick prototypes, since you pay per invocation and need no server management. Downsides are cold starts, execution time limits, connection handling with relational databases and harder local debugging. For steady high traffic or long-lived WebSocket connections, containers are usually cheaper and simpler. Many teams mix both.
186

How do you enable compression and why should you be careful with it?

Save to ▾
Show answer
Use the compression middleware or, better, let Nginx or the CDN compress responses with gzip or Brotli, which reduces JSON payload size by 70 percent or more. Be careful: compressing in Node uses CPU on the event loop, small responses gain little, and already compressed files like images should be skipped. Never compress responses mixing secrets with user input over HTTPS because of BREACH-style attacks.
187

How do you use the Node.js cluster module to use all CPU cores?

Save to ▾
Show answer
const cluster = require('cluster'); const os = require('os'); if (cluster.isPrimary) { for (let i = 0; i < os.availableParallelism(); i++) cluster.fork(); cluster.on('exit', (worker) => { console.log(`Worker ${worker.process.pid} died, restarting`); cluster.fork(); }); } else { require('./server'); // each worker listens on the same port } // In Kubernetes, prefer one process per container and more replicas.
188

What is an API gateway and what does it do in a Node.js microservice setup?

Save to ▾
Show answer
An API gateway is a single entry point in front of services. It routes requests, terminates TLS, validates tokens, applies rate limits, handles CORS, aggregates responses and collects logs and metrics. Examples are Kong, AWS API Gateway, Nginx and Envoy-based gateways. It keeps cross-cutting concerns out of each service, but must be highly available so it does not become a single point of failure.
189

Tell me about a time you had to balance speed of delivery with technical quality.

Save to ▾
Show answer
Situation: sales needed a partner API in one week for a client launch. Task: deliver fast without creating a mess. Action: I agreed with my manager to ship the core endpoints with validation, auth and tests, and to defer caching and admin tooling, logging them as tickets with dates. Result: the partner went live on time, and we completed the deferred work in the next sprint as planned.
190

How do you prioritise when multiple teams ask you for backend changes at the same time?

Save to ▾
Show answer
I list the requests with their business impact, deadlines and effort, then discuss priority with my manager or product owner rather than deciding alone. I communicate clear timelines to each team, look for shared solutions such as one generic endpoint serving two needs, and protect time for urgent production issues. This keeps expectations realistic and avoids half-finished work.
191

How do you prepare a Node.js app for production readiness review?

Save to ▾
Show answer
I check that config is validated, secrets are in a secret store, and logs are structured with request IDs. Health and readiness endpoints exist, graceful shutdown works, timeouts and retries are set on outbound calls, and rate limiting and helmet are enabled. Dashboards, alerts and runbooks are ready, load tests pass targets, backups are verified and a rollback plan is documented.
192

When would you use Kafka instead of a Redis-based queue like BullMQ?

Save to ▾
Show answer
BullMQ suits task queues inside one application: emails, reports, retries and scheduled jobs. Kafka is a distributed, durable event log for high-throughput streaming, where many consumer groups read the same events independently and can replay history, such as order events consumed by analytics, notifications and inventory. Kafka adds operational complexity, so use it when you need event streaming at scale.
193

Your API's p99 latency is high during peak hours even though CPU is only 40 percent. What could be wrong?

Save to ▾
Show answer
Low CPU with high latency suggests waiting, not computing. Check database query times and connection pool wait, slow downstream APIs, Redis latency, DNS lookups without keep-alive agents, garbage collection pauses and event loop lag from occasional heavy requests. Use distributed tracing with OpenTelemetry to see where time goes, then add indexes, caching, timeouts or more pool connections.
194

How do you achieve zero-downtime deployments for a Node.js service?

Save to ▾
Show answer
Use rolling or blue-green deployments so new instances start and pass readiness checks before old ones are removed. Implement graceful shutdown on SIGTERM so in-flight requests finish. Keep database migrations backward compatible using expand and contract. Use feature flags for risky changes and have automated rollback on error rate spikes. Canary releases send a small share of traffic first.
195

Your service is expected to get ten times normal traffic during a festive sale. How do you prepare?

Save to ▾
Show answer
Load test at above the expected peak, then fix bottlenecks. Pre-scale instances and database capacity instead of relying only on autoscaling, warm caches, and put static and catalogue responses on a CDN. Add rate limits, queue non-critical work, set timeouts and circuit breakers, prepare feature flags to switch off heavy features, and have dashboards, alerts and an on-call plan ready.
196

How would you migrate a monolithic Node.js app to microservices?

Save to ▾
Show answer
Do it gradually with the strangler fig pattern. Identify bounded contexts like auth, catalogue and payments, pick one with clear boundaries and high change rate, extract it behind an API gateway and route traffic to it. Give each service its own data, communicate through APIs and events, and invest in logging, tracing and CI first. Do not split before the monolith causes real pain.
197

How do you design an API that needs to handle 50,000 requests per second for reading product details?

Save to ▾
Show answer
Make the read path cache-heavy: CDN caching for public product pages, Redis cache for product details with high hit ratio, and database read replicas behind it. Run many stateless Node instances with autoscaling, keep responses small and compressed, and use keep-alive connections. Avoid cache stampedes with request coalescing or locks, and precompute data that is expensive to build.
198

How do you prevent a cache stampede when a popular key expires?

Save to ▾
Show answer
When a hot key expires, thousands of requests may hit the database at once. Solutions: allow only one request to rebuild the cache using a short Redis lock while others wait or serve stale data, refresh hot keys in the background before expiry, add random jitter to TTLs so keys do not expire together, and use stale-while-revalidate where slightly old data is acceptable.
199

Describe a time you led a technical decision that affected the whole team.

Save to ▾
Show answer
Situation: our five Node services used different logging and error formats, making incidents slow to debug. Task: as tech lead I wanted one standard. Action: I wrote a short RFC comparing options, built a shared package with pino, request IDs and an error handler, collected feedback and migrated one service as a pilot. Result: all services adopted it within a month and mean time to resolve incidents fell noticeably.
200

How do you optimise JSON serialisation for very large API responses?

Save to ▾
Show answer
Large JSON.stringify calls block the event loop. Reduce payload size with pagination and field selection first. For unavoidable large outputs, stream the response as NDJSON or a streamed JSON array, use schema-based fast serialisers like fast-json-stringify, which Fastify uses, and cache precomputed responses. Offload heavy transformation to worker threads or background jobs that write files to storage.

Frequently asked questions

What jobs can I get after learning Node.js?

You can apply for Node.js Developer, Backend Developer, MERN Full Stack Developer, API Developer and Integration Engineer roles. Startups, product companies, fintech, e-commerce and IT service firms hire Node.js developers for APIs, real-time features and microservices. With two to five years of experience you can move into Senior Backend Engineer or Tech Lead roles, especially if you also learn system design, cloud deployment and databases well.

Who is eligible to learn Full Stack with Node.js?

Students of BTech, BCA, MCA, BSc IT and similar courses, freshers from any graduation stream with interest in coding, and working professionals moving from testing, support or frontend roles can join. Basic computer skills and logical thinking are enough to start. Knowing HTML, CSS and basic JavaScript helps, but the course begins with JavaScript fundamentals before moving to Node.js and Express.

How long does it take to become job-ready in Node.js?

With regular practice of two to three hours a day, most learners become job-ready in about three to four months. The first month covers JavaScript and async concepts, the next two cover Express, databases, authentication and testing, and the last weeks focus on deployment and a full project. Building two or three real projects and practising interview questions matters more than finishing videos quickly.

Is Node.js still in demand in 2026?

Yes. Node.js remains one of the most used backend runtimes, powering APIs, BFF layers, real-time apps and serverless functions. Indian startups and product companies like it because teams can use JavaScript and TypeScript across frontend and backend. Demand is strongest for developers who combine Node.js with TypeScript, databases, cloud deployment, testing and good API design, rather than only basic CRUD skills.

Do I need to learn TypeScript and React along with Node.js?

For backend-only roles, strong JavaScript and Node.js are enough to start, but TypeScript is now expected in many job descriptions, so learn it after the basics. For full stack roles, React is the most common frontend pairing, forming the MERN stack. A good path is JavaScript, Node and Express first, then TypeScript, then React, building one full stack project that uses all of them.

Will I get a certificate and does it help in getting a job?

You receive a course completion certificate after finishing the training and projects, which you can add to your resume and LinkedIn profile. A certificate shows commitment, but recruiters mainly judge Node.js candidates on projects, GitHub code, problem solving and interview performance. Combine the certificate with deployed projects, a clean GitHub profile and solid answers to core backend questions for the best results.