Skip to content

Node.js Developer Interview Questions & Answers

40 questions for Node.js Developer interviews — 29 easy, 11 medium.

About the Node.js Developer role

Builds and maintains server-side applications and REST APIs using Node.js and Express. Works with product and frontend teams to implement features, integrates databases and third-party services such as payment gateways and SMS providers, writes tests and fixes production bugs. Freshers usually start with CRUD APIs and gradually own complete modules.

JavaScript ES6+ Express routing Async/await MongoDB basics Git
Solved for this roleStart 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 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.
02

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.
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

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.
05

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.
06

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.
07

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.
08

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.
09

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.
10

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.
11

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 });
12

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.
13

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.
14

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.
15

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.
16

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.
17

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.
18

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.
19

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.
20

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.
21

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.
22

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));
23

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.
24

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.
25

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.
26

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.
27

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'));
28

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.
29

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.
30

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.
31

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.
32

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.
33

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.
34

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.
35

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.
36

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) }); }
37

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.
38

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.
39

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.
40

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.

Frequently asked questions

What does a Node.js Developer do?

Builds and maintains server-side applications and REST APIs using Node.js and Express. Works with product and frontend teams to implement features, integrates databases and third-party services such as payment gateways and SMS providers, writes tests and fixes production bugs. Freshers usually start with CRUD APIs and gradually own complete modules.

Which skills are tested in a Node.js Developer interview?

Expect questions on JavaScript ES6+, Express routing, Async/await, MongoDB basics, Git. Most rounds also ask you to explain a project and solve one practical problem live.

How do I prepare for a Node.js Developer interview?

Revise Node.js Core and Event Loop, Asynchronous JavaScript, Express.js Framework first using the topic sheets, practise the questions on this page aloud, then do one full mock interview to practise timing.