Node.js Core and Event Loop Interview Questions for Full Stack with Node.js
25 Node.js Core and Event Loop questions with answers for full stack with node.js roles — 12 easy, 9 medium, 4 hard.
About Node.js Core and Event Loop
Understand how Node.js works inside: the V8 engine, the event loop and its phases, libuv thread pool, modules, npm, streams, buffers and events. These fundamentals explain why Node handles thousands of connections, why blocking code hurts every user, and how to use worker threads, graceful shutdown and memory debugging in real services.
Sheet progressStart 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?
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?
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.
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.
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.
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.
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?
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.
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?
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.
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.
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.
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?
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?
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?
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?
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?
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.
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?
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?
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?
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?
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.
Frequently asked questions
How important is Node.js Core and Event Loop in Full Stack with Node.js interviews?
Understand how Node.js works inside: the V8 engine, the event loop and its phases, libuv thread pool, modules, npm, streams, buffers and events. These fundamentals explain why Node handles thousands of connections, why blocking code hurts every user, and how to use worker threads, graceful shutdown and memory debugging in real services.
How should I use the Node.js Core and Event Loop sheet?
Go top to bottom — easy to hard. Answer before opening each answer, tick what you solved and save misses to Last-Minute Revision.