Episode 3 — NodeJS MongoDB Backend Architecture / 3.1 — Starting with NodeJS
3.1 — Interview Questions: Starting with Node.js
Common interview questions about Node.js fundamentals, the event loop, npm, modules, and package.json — with model answers for each.
< Exercise Questions | Quick Revision >
Beginner Level
Q1: What is Node.js? Is it a programming language?
Why interviewers ask: They want to see if you understand the fundamental nature of Node.js and can distinguish it from the language it runs.
Model answer: Node.js is not a programming language — it is an open-source, cross-platform JavaScript runtime environment built on Google's V8 engine. The language is JavaScript. Node.js is the runtime that lets JavaScript execute outside the browser by combining V8 (for compiling JS to machine code), libuv (for asynchronous I/O), and a set of built-in modules like fs, http, and crypto. Think of it this way: JavaScript is the language, Node.js is the stage where it performs.
Q2: What is the difference between Node.js and browser JavaScript?
Why interviewers ask: This tests whether you understand the different execution environments and their capabilities.
Model answer: Both environments run the same JavaScript language (ECMAScript), but they differ in the APIs they expose. Browser JavaScript has access to the DOM (document), the window object, and Web APIs like fetch and localStorage. Node.js has no DOM or window — instead, it provides server-side APIs like fs (file system), http (create servers), os (operating system info), and the process object. The global object is window in browsers and global in Node.js, though globalThis works in both. Node.js also supports CommonJS modules (require) alongside ES Modules (import), while browsers primarily use ES Modules.
Q3: What is npm? What happens when you run npm install?
Why interviewers ask: npm is used in every Node.js project — they want to confirm you understand the toolchain.
Model answer: npm is three things: a registry (the world's largest software registry at npmjs.com with over 2 million packages), a CLI tool (bundled with Node.js for installing, managing, and publishing packages), and a website (for searching and reading package documentation). When you run npm install, npm reads package.json to determine which packages are needed, queries the npm registry for each package and its sub-dependencies, downloads everything into the node_modules directory, and records exact versions in package-lock.json. If a package-lock.json already exists, npm uses it to ensure deterministic installs.
Q4: What is the difference between dependencies and devDependencies in package.json?
Why interviewers ask: This is a practical question that shows you understand deployment and production optimization.
Model answer: dependencies lists packages required for the application to run in production — things like Express, Mongoose, or bcrypt. devDependencies lists packages needed only during development — things like Jest (testing), ESLint (linting), Nodemon (auto-restart), and Prettier (formatting). The distinction matters in production: running npm install --production or setting NODE_ENV=production skips devDependencies entirely, resulting in a smaller node_modules, faster deployments, and a reduced attack surface. As a rule: if your running application needs the package to function, it goes in dependencies. If it is only a development tool, it goes in devDependencies.
Intermediate Level
Q5: Explain the Node.js event loop. How does Node.js handle thousands of concurrent connections with a single thread?
Why interviewers ask: This is the most fundamental Node.js concept. Every senior developer must understand it.
Model answer: Node.js runs JavaScript on a single thread, but it achieves concurrency through the event loop and non-blocking I/O. When your code initiates an I/O operation (reading a file, querying a database, making an HTTP request), Node.js does not wait for it to complete. Instead, it delegates the operation to libuv — which either uses OS-level async mechanisms (epoll on Linux, kqueue on macOS) for network operations or a thread pool (4 threads by default) for file system and DNS operations — and immediately moves on to execute the next line of code. When the I/O completes, a callback is placed on a queue. The event loop continuously checks: "Is the call stack empty? If yes, pick up the next callback from the queue and execute it." This means one thread can handle thousands of connections because it never blocks waiting for I/O. The restaurant analogy works well: one waiter (event loop) takes orders from many tables and hands them to the kitchen (libuv/OS), rather than standing at each table waiting for food.
Q6: What is the difference between CommonJS and ES Modules in Node.js?
Why interviewers ask: Module systems are fundamental to how Node.js code is organized, and the ecosystem is in transition.
Model answer: CommonJS (CJS) is the original module system in Node.js, using require() to import and module.exports to export. It loads modules synchronously and is the default behavior. ES Modules (ESM) use import/export syntax, load asynchronously, support top-level await, and enable tree-shaking (dead code elimination by bundlers). To use ESM in Node.js, you either set "type": "module" in package.json (making all .js files use ESM) or use the .mjs extension. To use CJS in an ESM project, use the .cjs extension. One practical difference: __dirname and __filename are available in CJS but not in ESM — in ESM you use import.meta.url with fileURLToPath() instead. New projects should prefer ESM since it is the JavaScript standard, but CJS remains dominant in the existing ecosystem.
Q7: What is package-lock.json and why should you commit it?
Why interviewers ask: This tests your understanding of deterministic builds and team collaboration.
Model answer: package-lock.json records the exact version of every installed package and its entire dependency tree. Without it, different developers running npm install at different times could get different package versions (because ^4.18.2 in package.json matches any version from 4.18.2 up to but not including 5.0.0). This leads to "works on my machine" bugs. You should always commit package-lock.json to version control so every developer and CI/CD pipeline gets the exact same dependency versions. In CI/CD, you should use npm ci instead of npm install — it installs strictly from the lock file, deletes node_modules first, and fails if the lock file is out of sync with package.json. This ensures reproducible, deterministic builds.
Q8: What is the process object and what are its most important properties?
Why interviewers ask: The process object is Node.js-specific and essential for building production applications.
Model answer: process is a global object in Node.js (available without require) that provides information about and control over the current Node.js process. Key properties and methods include: process.argv (an array of command-line arguments, where index 0 is the node path and index 1 is the script path), process.env (an object containing environment variables like NODE_ENV, PORT, DATABASE_URL), process.cwd() (the directory from which the process was launched), process.exit(code) (terminates the process with an exit code — 0 for success, non-zero for error), process.pid (the process ID), process.version (the Node.js version string), and process.memoryUsage() (heap and RSS memory statistics). For event handling, process.on('uncaughtException') catches unhandled errors, process.on('SIGINT') handles Ctrl+C for graceful shutdown, and process.nextTick(callback) schedules a callback to run before any I/O in the next event loop iteration.
Advanced Level
Q9: Explain the phases of the Node.js event loop. What is the difference between setTimeout, setImmediate, and process.nextTick?
Why interviewers ask: This probes deep understanding of Node.js internals and asynchronous execution order.
Model answer: The event loop runs through six phases in order: (1) timers — executes setTimeout and setInterval callbacks whose threshold has elapsed, (2) pending callbacks — executes I/O callbacks deferred from the previous cycle, (3) idle/prepare — internal use, (4) poll — retrieves new I/O events and executes I/O callbacks, (5) check — executes setImmediate callbacks, (6) close callbacks — like socket.on('close'). After each phase, the microtask queue is drained (Promises and process.nextTick).
The differences: setTimeout(fn, 0) schedules fn in the timers phase of the next (or current) event loop iteration. setImmediate(fn) schedules fn in the check phase, which comes right after the poll phase. process.nextTick(fn) is not part of the event loop at all — it fires after the current operation completes and before the event loop continues, even before Promises. The execution order is: synchronous code first, then process.nextTick callbacks, then resolved Promise callbacks, then setTimeout/setImmediate (whose relative order depends on context). In an I/O callback, setImmediate always fires before setTimeout(fn, 0) because the check phase comes right after poll.
Q10: Node.js is single-threaded. How does it handle CPU-intensive tasks without blocking the event loop?
Why interviewers ask: This is a classic trap question that separates surface-level knowledge from deep understanding.
Model answer: While Node.js runs JavaScript on a single thread, there are several strategies for CPU-intensive work: (1) Worker Threads (worker_threads module) — spawn actual OS threads that run JavaScript in parallel, each with its own V8 instance, and communicate via message passing or SharedArrayBuffer. (2) The Cluster module — forks the Node.js process across all CPU cores, each with its own event loop, sharing the same server port. (3) Child Processes (child_process module) — spawn separate processes to offload heavy computation, communicating via IPC. (4) Offloading to native modules — libraries like bcrypt use C++ bindings that run on the libuv thread pool, not the main thread. (5) External services — delegate computation to microservices written in languages better suited for CPU work (Go, Rust, Python). The key principle: never block the event loop with synchronous computation lasting more than a few milliseconds. For anything heavy, offload to a worker, a child process, or an external service.
Q11: How would you design the package.json for a production Node.js application that needs to support both development and CI/CD workflows?
Why interviewers ask: This tests practical engineering judgment about project configuration.
Model answer: A production-ready package.json should include: "private": true to prevent accidental publishing. "type": "module" for modern ES Module syntax. An engines field specifying the minimum Node.js version (e.g., "node": ">=20"), enforced by engine-strict=true in .npmrc. Scripts should cover the full workflow: start (production server), dev (watch mode with env loading: node --watch --env-file=.env), test (with coverage flags), lint and lint:fix, format, build (if using TypeScript), and pretest (runs lint before tests). Dependencies should be carefully split — runtime packages like express and mongoose in dependencies, tooling like jest, eslint, and typescript in devDependencies. The .npmrc should also set save-exact=true if the team wants pinned versions. Crucially, package-lock.json must be committed, and CI/CD pipelines should use npm ci (not npm install) for deterministic builds. For a monorepo, add workspaces to manage multiple packages.
Quick-Fire Questions
| # | Question | Key Point |
|---|---|---|
| 1 | What does V8 do? | Compiles JavaScript to machine code using JIT compilation |
| 2 | What is libuv written in? | C |
| 3 | Default libuv thread pool size? | 4 threads (configurable via UV_THREADPOOL_SIZE) |
| 4 | What does npm init -y do? | Creates package.json with all default values |
| 5 | What is npx for? | Run packages without installing them globally |
| 6 | What does ^ mean in ^4.18.2? | Allow minor and patch updates (up to, but not including, 5.0.0) |
| 7 | Where does npm install put packages? | node_modules/ directory |
| 8 | What is process.exit(1)? | Exit with error code 1 (non-zero = failure) |
| 9 | LTS stands for? | Long-Term Support |
| 10 | __dirname vs process.cwd()? | File location vs command execution location |
| 11 | How to use ESM in Node.js? | Set "type": "module" in package.json or use .mjs extension |
| 12 | What file should you NEVER commit? | node_modules/ |
| 13 | npm install vs npm ci? | ci is stricter — installs exactly from lock file, faster in CI |
| 14 | What is the global object in Node.js? | global (or globalThis for cross-environment) |
| 15 | Who created Node.js and when? | Ryan Dahl, 2009 (presented at JSConf EU) |
<- Back to 3.1 — Starting with Node.js (README)