Beginner Level (0–1 Years)

1. What happens if you use a blocking operation like `fs.readFileSync()` in an API route handler?

Answer:

Using fs.readFileSync() in an API route handler pauses the program until the file is read, slowing down the app and preventing it from handling other requests. Node.js uses an event loop to manage tasks, and blocking operations stop it from running smoothly. Use fs.readFile() for non-blocking file operations.

2. Can you require a module before it’s fully defined in the same file?

Answer:

Node.js loads modules synchronously, but if you require a module before its exports are fully defined in the same file, you may get an incomplete or empty object. This happens because Node.js creates a module object early but fills it as the code runs.

3. Is Node.js single-threaded or multi-threaded?

Answer:

Node.js runs JavaScript code in a single thread, meaning it processes one task at a time. However, it handles asynchronous operations like file reading or network requests in the background using a thread pool, making it seem multi-threaded for I/O tasks.

4. What does `require.cache` do, and how can it cause issues?

Answer:

require.cache stores loaded modules so requiring them again uses the cached version. This can cause issues if a module has mutable state, like an array that’s modified. For example, if one part of the app changes the array, other parts see the change unexpectedly.

5. Why should you avoid using global variables in Node.js modules?

Answer:

Global variables can be accidentally changed by other modules or code, causing bugs that are hard to track. They also break module encapsulation. Instead, use module.exports to share data explicitly between modules.

6. What’s the difference between `process.nextTick()` and `setImmediate()`?

Answer:

process.nextTick() runs a callback immediately after the current operation, before other tasks like I/O. setImmediate() runs after I/O tasks. Overusing process.nextTick() can delay other tasks, slowing the app.

7. In JavaScript, if you have a sequence of operations that includes immediate console logs, a setTimeout with a delay of 0, and a Promise that resolves immediately, in what order will the outputs appear?

Answer:

The output is A, D, C, B. A and D log first synchronously. C logs next because Promises run in the microtask queue, which has priority. B logs last because setTimeout uses the macrotask queue.

8. What is the purpose of `module.exports` in Node.js?

Answer:

module.exports defines what a module shares with other parts of the application. It allows you to export functions, objects, or values so other modules can use them via require(). For example, module.exports = { add: (a, b) => a + b };.

9. Can two modules share state? How?

Answer:

Yes, if a module exports a mutable object like an array and another module imports it, they share the same reference. Changes in one module affect the other. This is usually avoided to prevent unexpected behavior.

10. What issue arises when a JavaScript module exports a single instance of a class instead of the class itself or a factory function?

Answer:

Exporting a single instance creates a singleton, meaning all modules using it share the same state. This can cause issues if different parts need separate instances. Instead, export the class or a factory function: module.exports = SomeClass;.

11. How can a memory leak occur in a Node.js application?

Answer:

Memory leaks happen when objects like global variables, unremoved event listeners, or large data in closures aren’t released, preventing garbage collection. For example, adding event listeners without removing them can keep objects in memory.

12. Why is using `await` inside a loop risky?

Answer:

Using await in a loop makes each iteration wait for the previous one, slowing execution. For faster parallel processing, collect Promises in an array and use Promise.all().

13. How do you handle errors in an async function in Node.js?

Answer:

Use a try/catch block to handle errors in async functions. For example:

async function fetchData() {
  try {
    const result = await someAsyncOperation();
    return result;
  } catch (error) {
    console.error('Error:', error);
  }
}

14. Why might `console.log()` not show up in a production log file?

Answer:

Logs may not appear due to output buffering, misconfigured logging (e.g., redirected to /dev/null), or the app running as a daemon without proper log setup. Use a logging library like Winston for reliable logging.

15. How can you create a truly private variable in a Node.js module?

Answer:

Use a closure by defining a variable inside the module’s scope without exporting it:

let secret = 'hidden';
module.exports = {
  getSecret: () => secret
};

16. What is the `EventEmitter` class in Node.js, and how is it used?

Answer:

The EventEmitter class enables event-driven programming by letting objects emit and listen for events. Example:

const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('greet', () => console.log('Hello!'));
emitter.emit('greet');

17. What’s the difference between callbacks and Promises in Node.js?

Answer:

Callbacks are functions passed to handle async results, but they can lead to nested code (“callback hell”). Promises use .then() and .catch() for cleaner chaining and error handling, making async code easier to read.

18. What issue arises when a JavaScript module starts an Express server immediately before exporting the application instance?

Answer:

Starting the server immediately before exporting can cause issues in tests where you want to control when the server starts. Instead, export the app and call listen separately.

19. Can you use ES modules (import/export) in Node.js?

Answer:

Yes, use .mjs files or set "type": "module" in package.json to use import/export. Otherwise, Node.js uses CommonJS (require/module.exports).

20. What’s the default scope of a variable in a Node.js module?

Answer:

Variables declared with var, let, or const at the top level of a module are private to that module’s scope and not accessible outside unless exported.

21. Why doesn’t `console.log(this)` print the same result inside and outside a function in Node.js?

Answer:

At the module’s top level, this refers to module.exports. Inside a function, this is the global object (or undefined in strict mode), due to how Node.js handles module scope.

22. What’s a potential pitfall of using `JSON.stringify()` on a circular object?

Answer:

It throws a TypeError because circular references (e.g., an object referencing itself) can’t be serialized. Use libraries like flatted or handle circular references manually.

23. Why might `uncaughtException` be dangerous to rely on?

Answer:

Using uncaughtException can leave the app in an unstable state, as it doesn’t fix the root cause. Best practice is to log the error and exit, letting a process manager restart the app.

24. How do you create a simple Express route in Node.js?

Answer:

Use Express to define routes with HTTP methods like get. Example:

const express = require('express');
const app = express();
app.get('/hello', (req, res) => res.send('Hello World!'));
app.listen(3000);

25. In Node.js, what does it indicate when a module checks if it is the main entry point of the application?

Answer:

It returns true if the module is run directly (e.g., node script.js), not required by another module. It’s used to run code only when the module is the entry point.


👋 Need top Node.js developers for your project? Interview this week!

Fill out the form to book a call with our team. We’ll match you to the top developers who meet your requirements, and you’ll be interviewing this week!

Request for Service


Intermediate Level (1–3 Years)

1. What’s the difference between `spawn` and `exec` in the `child_process` module?

Answer:

spawn streams data (stdout, stderr) and is ideal for large outputs, e.g., spawn('ls', ['-l']). exec buffers output in a shell, e.g., exec('ls -l', callback), but can cause memory issues with large outputs.

2. How can you handle uncaught exceptions in a Node.js app properly?

Answer:

Use process.on('uncaughtException') and process.on('unhandledRejection') for logging and shutdown, but avoid continuing execution. Prefer try/catch or .catch() for structured error handling.

3. Why should you avoid using synchronous file methods in production?

Answer:

Synchronous methods like fs.readFileSync block the event loop, halting other requests and reducing scalability. Use asynchronous methods like fs.readFile instead.

4 System: . How would you implement rate limiting in a Node.js API?

Answer:

Use middleware like express-rate-limit to track requests per IP and enforce limits. For distributed apps, store counts in Redis to synchronize across instances. Example: const rateLimit = require('express-rate-limit'); app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));

5. What’s the role of the `EventEmitter` class, System: , and when would you use it?

Answer:

EventEmitter enables event-driven communication, allowing objects to emit and listen for events. Use it for async tasks like stream processing or custom notifications. Example: const EventEmitter = require('events'); const emitter = new EventEmitter(); emitter.on('data', () => console.log('Event fired')); emitter.emit('data');

6. Why might an Express route handler with async logic hang without returning a response?

Answer:

Uncaught errors in async handlers don’t trigger next(), leaving the request hanging. Use try/catch to catch errors and call next(err) or send a response. Example: app.get('/route', async (req, res, next) => { try { const data = await someAsyncFn(); res.send(data); } catch (err) { next(err); } });

7. How does `cluster` help in scaling a Node.js application?

Answer:

cluster creates worker processes to utilize multiple CPU cores, sharing the same port. Each worker runs independently, improving performance for concurrent requests. Example: const cluster = require('cluster'); if (cluster.isMaster) { cluster.fork(); } else { require('./server'); }

8. In JavaScript, when combining immediate console logs, a setImmediate call, a setTimeout with zero delay, and an immediately resolved Promise, in what order will their outputs be logged?

Answer:

0, 1, promise, timeout, immediate. Synchronous code runs first, then microtasks (promise) in the microtask queue, then timers (setTimeout) in the timers phase, and finally setImmediate in the check phase.

9. What are the implications of modifying `module.exports` vs `exports`?

Answer:

exports is a reference to module.exports. Adding properties to exports works (e.g., exports.foo = 1), but reassigning it (e.g., exports = {}) breaks the link. Use module.exports = {} for full exports.

10. How do you implement JWT authentication in an Express API?

Answer:

Use jsonwebtoken to create and verify tokens:

const jwt = require('jsonwebtoken');
const auth = (req, res, next) => {
  const token = req.header('Authorization').replace('Bearer ', '');
  try {
    req.user = jwt.verify(token, process.env.JWT_SECRET);
    next();
  } catch (e) {
    res.status(401).send('Unauthorized');
  }
};
app.use('/protected', auth);

11. What is middleware in Express, and how does its order affect the app?

Answer:

Middleware processes requests in the order defined. For example, authentication middleware must run before route handlers to ensure access control. Incorrect ordering can skip logic or cause errors.

12. Why is it important to return or call `next()` in an Express middleware?

Answer:

Without calling next() or sending a response, the request hangs, preventing further middleware or routes from executing, leading to client timeouts.

13. How can you share sessions across multiple Node.js instances?

Answer:

Use a session store like Redis to centralize session data:

const session = require('express-session');
const RedisStore = require('connect-redis')(session);
app.use(session({ store: new RedisStore({ client: redis }), secret: 'secret' }));

This ensures consistency across clustered or load-balanced instances.

14. What causes a ‘heap out of memory’ error in Node.js?

Answer:

This occurs when the app exceeds V8’s memory limit, often due to memory leaks (e.g., unremoved event listeners), large objects (e.g., huge arrays), or excessive concurrent requests. Use --max-old-space-size to increase limits or optimize memory usage.

15. What’s the role of `package-lock.json`?

Answer:

package-lock.json locks dependency versions, ensuring consistent installs across environments by recording the exact dependency tree, preventing version mismatches.

16. What are circular dependencies and how do they affect Node.js modules?

Answer:

Circular dependencies occur when modules depend on each other, causing Node.js to cache incomplete exports, leading to undefined values. Avoid by refactoring to a third module or delaying imports.

17. How can you load environment variables in a Node.js app?

Answer:

Use dotenv to load variables from冥 System: from a `.env` file into process.env. Example: require('dotenv').config(). Avoid hardcoding secrets in code.

18. How would you implement request logging in an Express app?

Answer:

Use middleware like morgan or custom logging:

const morgan = Reuire('morgan');
app.use(morgan('combined'));

Or manually: app.use((req, res, next) => { console.log(req.method, req.url); next(); });.

19. What is the difference between `Buffer.alloc()` and `Buffer.allocUnsafe()`?

Answer:

Buffer.alloc(size) initializes memory to zeros, ensuring clean data. Buffer.allocUnsafe(size) is faster but may contain old data. Example: const buffer = Buffer.alloc(10); console.log(buffer); // .

20. How can you test an Express route without starting the server?

Answer:

Use supertest to test the app instance directly:

const request = require('supertest');
const app = require('./app');
request(app).get('/').expect(200).end(done);

21. How does streaming in Node.js improve performance?

Answer:

Streaming processes data in chunks, reducing memory usage. Example: fs.createReadStream('file.txt').pipe(res) handles large files efficiently compared to fs.readFile.

22. What is the difference between `readable.pipe(writable)` and manually handling `data` and `end` events?

Answer:

pipe() automates data flow and backpressure, simplifying streaming. Manual handling with on('data') and write() requires explicit backpressure management, e.g., pausing the stream if the writable buffer is full.

23. Why use `setImmediate()` instead of `setTimeout(fn, 0)`?

Answer:

setImmediate() runs after the current event loop phase (check phase), while setTimeout(fn, 0) runs in the timers phase. setImmediate is more predictable for immediate callbacks. Example: setImmediate(() => console.log('Now'));.

24. What is the benefit of using `promisify()` from the `util` module?

Answer:

promisify converts callback-based functions to Promises, enabling async/await. Example: const readFile = require('util').promisify(fs.readFile);.

25. What are weak references and when would you use `WeakMap` in Node.js?

Answer:

WeakMap allows keys to be garbage-collected if no other references exist. Use for private data tied to objects, e.g., caching metadata: const wm = new WeakMap(); wm.set(obj, { data: 'private' }); // obj can be GC'd.

26. How does the event loop handle timers and I/O callbacks?

Answer:

The event loop processes timers in the timers phase and I/O callbacks in the poll phase. Microtasks (e.g., Promises) run between phases, prioritizing faster operations.

27. Why might a Promise never resolve or reject in Node.js?

Answer:

Forgetting to call resolve or reject causes a Promise to hang. Example: new Promise(() => { /* no resolve/reject */ }) never completes.

28. How do you debug memory leaks in Node.js?

Answer:

Use process.memoryUsage() to monitor memory, or tools like Chrome DevTools or heapdump to analyze heap snapshots and find unreleased objects.

29. What is the purpose of `process.env.NODE_ENV` and how should it be used?

Answer:

process.env.NODE_ENV indicates the environment (e.g., ‘development’, ‘production’). Use it to enable debugging in development or optimizations in production.

30. What’s the advantage of `async`/`await` over raw Promises?

Answer:

async/await offers cleaner, synchronous-like syntax, simplifying error handling and avoiding nested .then() chains. Example: async () => { try { await fn(); } catch (e) { handle(e); } }.

31. How would you implement a custom error class in Node.js?

Answer:

Create a class extending Error:

class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    Error.captureStackTrace(this, this.constructor);
  }
}

Error.captureStackTrace preserves the stack trace.

32. How can you ensure graceful shutdown of a Node.js server?

Answer:

Handle signals and close resources:

process.on('SIGINT', () => {
  server.close(() => {
    db.disconnect();
    process.exit(0);
  });
});

33. How does `require.resolve()` work?

Answer:

require.resolve() returns the full path of a module without loading it, useful for path inspection or cache clearing. Example: console.log(require.resolve('fs'));.

34. What does `Object.freeze()` do, and when is it useful in Node.js?

Answer:

Object.freeze() makes an object immutable. Useful for protecting config objects: const config = Object.freeze({ port: 3000 });.

35. What’s the difference between `exports` and `module.exports` in CommonJS?

Answer:

exports is a reference to module.exports. Reassigning exports breaks the link; use module.exports for full exports. Example: module.exports = { foo: 1 };.

36. How would you secure sensitive environment variables in Node.js?

Answer:

Use dotenv with .env files, exclude from version control via .gitignore, and use secret managers (e.g., AWS Secrets Manager) in production.

37. Why does this `await` statement not catch an error?

Answer:

try {
  await someAsyncFn().catch(e => console.log(e));
} catch (err) {
  console.log('Caught', err);
}

The inner .catch() handles the error, preventing the outer try/catch from catching it. Remove the inner .catch().

38. What’s a typical use case for `child_process.fork()`?

Answer:

fork() spawns a Node.js process with IPC, ideal for CPU-intensive tasks like image processing: const cp = require('child_process'); cp.fork('./worker.js');.

39. What is the difference between `__dirname` and `process.cwd()`?

Answer:

__dirname is the directory of the current module; process.cwd() is the working directory of the Node process. Example: console.log(__dirname); vs. console.log(process.cwd());.

40. How can you detect if your app is running inside a Docker container?

Answer:

Check for Docker-specific markers: const isDocker = require('fs').existsSync('/.dockerenv') || process.env['DOCKER_CONTAINER'];.

41. Why is it dangerous to trust `req.body` in Express apps?

Answer:

req.body can contain malicious or malformed data. Always validate and sanitize inputs to prevent injection attacks or errors.

42. What causes ‘EMFILE: too many open files’ error?

Answer:

It occurs when too many files or sockets are open, exceeding OS limits. Fix by managing streams or increasing limits with ulimit -n 2048.

43. What’s the difference between soft and hard dependencies in `package.json`?

Answer:

dependencies are required for runtime; devDependencies are for development. optionalDependencies are non-critical, and peerDependencies are expected by peer packages.

44. How does backpressure work in Node.js streams?

Answer:

Backpressure pauses a readable stream when the writable stream’s buffer is full, preventing memory overload. Example: readStream.pause() when writable.write() returns false.

45. How would you trace async call stacks in a Node app?

Answer:

Use console.trace() or --inspect with Chrome DevTools to debug async operations, providing better stack traces for Promises and callbacks.

46. How do you design a RESTful API in Node.js using Express?

Answer:

Follow REST principles: use HTTP methods, consistent endpoints, and stateless routes. Example:

app.get('/users/:id', (req, res) => res.json({ id: req.params.id }));
app.post('/users', (req, res) => res.status(201).json(req.body));

47. How can you implement middleware to validate request bodies in Express?

Answer:

Use express-validator or custom middleware:

const validate = (req, res, next) => {
  if (!req.body.name) return res.status(400).send('Name required');
  next();
};
app.post('/users', validate, (req, res) => res.json(req.body));

48. How do you handle large file uploads in a Node.js application?

Answer:

Use multer with streaming:

const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('file'), (req, res) => res.send('Uploaded'));

49. How do you write unit tests for a Node.js application?

Answer:

Use jest or mocha:

const sum = (a, b) => a + b;
test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

50. How can you implement caching in a Node.js API?

Answer:

Use node-cache or Redis:

const NodeCache = require('node-cache');
const cache = new NodeCache();
app.get('/data', (req, res) => {
  const key = 'data';
  if (cache.has(key)) return res.json(cache.get(key));
  const data = { value: 'expensive' };
  cache.set(key, data, 60);
  res.json(data);
});

LATAM-Image-V2.png

Hire Top LATAM Developers: Guide

We’ve prepared this guide that covers  benefits, costs, recruitment, and remote team management to a succesful hiring of developers in LATAM. 

Fill out the form to get our guide.

Gated content


Advanced Level (3+ Years)

1. How does Node.js handle the ‘tick’ and ‘microtask’-jaques differently in the event loop?

Answer:

process.nextTick() runs in the microtask queue before Promises and I/O or timer callbacks, potentially starving the event loop if overused. setImmediate() runs in the check phase, after I/O, ensuring other tasks are processed first.

2. Explain the internal difference between asynchronous I/O and non-blocking I/O in Node.js.

Answer:

Non-blocking I/O allows the main thread to continue without waiting for I/O completion. Asynchronous I/O, managed by libuv, offloads work to background threads or OS events, using callbacks or Promises. Example: fs.readFile uses async I/O via the thread pool.

3. What is the impact of using too many listeners in an EventEmitter, and how can you mitigate it?

Answer:

Too many listeners on an EventEmitter trigger a memory leak warning. Mitigate by increasing the limit with emitter.setMaxListeners(n) or refactoring to reduce listeners, e.g., using a single handler to dispatch events.

4. What is a domain in Node.js and why is it deprecated?

Answer:

Domains grouped async operations for shared error handling but were deprecated due to unpredictable error routing across contexts. Use async/await with try/catch or Express error middleware instead.

5. How does V8 optimize frequently called JavaScript functions?

Answer:

V8 uses inline caching, hidden classes, and JIT compilation (TurboFan) to optimize hot functions. Deoptimization occurs if assumptions (e.g., stable types) are broken, slowing execution.

6. How would you profile CPU usage in a Node.js app?

Answer:

Use --inspect with Chrome DevTools, or tools like clinic, 0x, or node --cpu-prof to generate flame graphs and identify CPU-intensive functions.

7. How can asynchronous context be lost and how do you preserve it?

Answer:

Context loss occurs in callbacks or Promises without shared state. Preserve it with request-scoped objects, async_hooks, or libraries like cls-hooked. Example: Store user data in req.locals in Express.

8. How does the garbage collector know when to collect an object in Node.js?

Answer:

V8’s garbage collector uses mark-sweep and scavenging to collect unreachable objects. Cyclic references are collected if no strong references remain from the root (e.g., global, stack).

9. What is a native addon in Node.js and when would you use one?

Answer:

Native addons are C/C++ modules using node-addon-api or N-API. Use for system library integration (e.g., libxml2) or performance-critical tasks like cryptography.

10. Why might Promises lead to memory leaks in long-lived applications?

Answer:

Unresolved Promises holding large closures (e.g., new Promise(resolve => { let arr = new Array(1e6); })) prevent garbage collection. Ensure all Promises resolve or reject.

11. What is the role of the libuv library in Node.js?

Answer:

Libuv provides the event loop, thread pool, and async I/O mechanisms, enabling non-blocking operations for file systems, networking, and timers in Node.js.

12. How does Node.js ensure consistency in `fs.watch()` across different platforms?

Answer:

fs.watch() uses platform-specific APIs (e.g., inotify on Linux, ReadDirectoryChangesW on Windows), causing inconsistent event granularity. Use chokidar for reliable cross-platform watching.

13. What are the limitations of using `worker_threads` in Node.js?

Answer:

worker_threads don’t share the event loop or context, requiring explicit data transfer via SharedArrayBuffer or messages, which can lead to synchronization and error-handling complexity.

14. How would you throttle and debounce a high-frequency event stream in Node.js?

Answer:

Use lodash.throttle or debounce, or custom timers. Throttling limits execution rate; debouncing delays until inactivity. Example: _.throttle(fn, 1000).

15. What happens if you perform heavy computation on the main thread in Node.js?

Answer:

It blocks the event loop, delaying I/O and timers. Offload to worker_threads or external services to maintain responsiveness.

16. How can you track down a memory leak in production Node.js applications?

Answer:

Use heapdump, clinic, or memwatch-next to capture heap snapshots. Analyze retained objects and growth trends with Chrome DevTools or V8 profiling.

17. How do you implement zero-downtime deployments in Node.js?

Answer:

Use a load balancer and rolling deployments with cluster or PM2. Handle SIGTERM to close connections gracefully:

process.on('SIGTERM', () => server.close(() => process.exit(0)));

18. What are the consequences of using global state in a clustered Node.js app?

Answer:

Global state isn’t shared across cluster workers, causing inconsistencies. Use external stores like Redis or databases for shared state.

19. Why might `async_hooks` significantly slow down a Node.js application?

Answer:

async_hooks adds 10-20% overhead per async operation by tracking resources. Use lightweight logging or tracing libraries like OpenTelemetry for production.

20. How would you design a fault-tolerant queue-based job system in Node.js?

Answer:

Use RabbitMQ or Redis for queuing, with retries, exponential backoff, and dead-letter queues. Persist jobs in durable storage to handle failures.

21. Explain the difference between deep and shallow cloning in JavaScript.

Answer:

Shallow cloning copies top-level properties; deep cloning copies nested objects. Use structuredClone or lodash.cloneDeep(). JSON.parse(JSON.stringify(obj)) fails with non-serializable data.

22. What are the security risks of exposing stack traces to users?

Answer:

Stack traces reveal file paths, internal logic, or library versions, aiding attackers. Sanitize errors in production with custom error handlers.

23. How can you prevent prototype pollution in a Node.js app?

Answer:

Validate inputs, avoid unsafe object merging (e.g., Object.assign), and use libraries like deepmerge. Example: Check for __proto__ in user input.

24. What is the difference between `res.end()`, `res.send()`, and `res.json()` in Express?

Answer:

res.end() closes the response without formatting. res.send() handles strings, buffers, or objects. res.json() sends JSON with proper headers.

25. What is an example of a race condition in asynchronous JavaScript code?

Answer:

Concurrent DB updates without locks, e.g., await db.update({ balance: balance - 10 }), can overwrite each other. Use transactions or locks to synchronize.

26. Why would you use the `vm` module in Node.js, and what are the dangers?

Answer:

vm runs JavaScript in a sandbox for plugins or eval. Misconfiguration can allow code escape, enabling arbitrary execution. Use strict context isolation.

27. How do you implement distributed tracing in a Node.js microservices architecture?

Answer:

Use OpenTelemetry or Jaeger:

const { trace } = require('@opentelemetry/api');
const tracer = trace.getTracer('my-service');
app.get('/route', (req, res) => {
  const span = tracer.startSpan('handle-request');
  // Process request
  span.end();
  res.send('Done');
});

28. How can you detect and handle file descriptor leaks?

Answer:

Monitor with lsof or process.getActiveResourcesInfo(). Ensure streams and sockets are closed using .destroy() or max open limits.

29. What’s a weak map memory leak pattern, and how do you avoid it?

Answer:

Storing objects in WeakMap with external references (e.g., wm.set(obj, data); global.ref = obj;) prevents GC. Avoid strong references elsewhere.

30. Why is reentrancy a problem in async code, and how can it manifest?

Answer:

Reentrant async callbacks can modify shared state unexpectedly, e.g., updating an array during async iteration. Use locks or queue tasks to prevent overlap.

31. How do you safely expose configuration values to both client and server in a fullstack Node app?

Answer:

Use a shared module with whitelisted safe values:

module.exports = { public: Object.freeze({ apiUrl: process.env.API_URL }) };

Avoid exposing secrets to the client.

32. What are the trade-offs between using HTTP/2 and HTTP/1.1 in Node.js?

Answer:

HTTP/2 offers multiplexing and header compression but has complex state management and less proxy support. HTTP/1.1 is simpler but less efficient for concurrent requests.

33. How can you optimize the Node.js thread pool for I/O-heavy workloads?

Answer:

Set process.env.UV_THREADPOOL_SIZE (e.g., 8) based on workload (disk vs. network I/O). Use profiling to monitor thread contention and adjust.

34. How would you implement a plugin system in a Node.js application?

Answer:

Define a plugin interface and dynamically load modules:

const plugins = fs.readdirSync('plugins').map(file => require(`./plugins/${file}`));
plugins.forEach(plugin => plugin.init(app));

Use vm for untrusted plugins.

35. What is the purpose of `dns.lookup()` vs `dns.resolve()` in Node.js?

Answer:

dns.lookup() uses OS resolution for IPs; dns.resolve() queries DNS servers for specific records (e.g., MX, TXT). Example: dns.resolve('example.com', 'MX').

36. Why might a Node.js server appear responsive but still be overloaded?

Answer:

Intermittent event loop blocking or failing health checks can occur while accepting connections. Use metrics (e.g., Prometheus) and profiling to diagnose.

37. What’s the difference between `stream.pipeline()` and manually piping streams?

Answer:

stream.pipeline() handles errors and cleanup automatically, safer for complex chains. Manual piping requires explicit error handling:

readStream.on('error', handleError).pipe(writeStream);

38. How does Node.js handle backpressure in HTTP streams?

Answer:

It pauses readable streams when the writable buffer is full. Use pipe() for automatic handling or manage .pause()/resume() manually.

39. What is the effect of using `–max-old-space-size`?

Answer:

It sets V8’s heap size limit (e.g., node --max-old-space-size=4096 for 4GB). Useful for memory-intensive apps or containers but increases GC pauses.

40. What are some alternatives to the built-in `cluster` module for scaling?

Answer:

Use PM2, Docker with load balancers, worker_threads, or service mesh (e.g., Istio) for distributed scaling and orchestration.

41. How would you prevent SSRF vulnerabilities in a Node.js service making external requests?

Answer:

Validate URLs, use allowlists, block internal IPs, and set timeouts with libraries like axios:

axios.get(url, { timeout: 5000, validateStatus: status => status < 300 });

42. What are some causes of event loop lag, and how do you measure it?

Answer:

Causes include synchronous code, large loops, or blocking I/O. Measure with event-loop-lag or PerformanceObserver to track delays.

43. When should you prefer a message broker over REST in microservices?

Answer:

Use brokers (e.g., RabbitMQ) for async, reliable, and decoupled communication. REST is better for synchronous, request-response interactions.

44. How can you limit CPU usage of a Node.js process?

Answer:

Use cpulimit, container CPU quotas, or worker_threads to isolate heavy tasks. Example: docker run --cpus="1.0".

45. What’s a typical use for `Object.defineProperty()` in Node.js?

Answer:

Create non-enumerable or read-only properties:

Object.defineProperty(obj, 'key', { value: 'val', writable: false });

Useful for library APIs or immutable configs.

46. How would you debug race conditions in concurrent async code?

Answer:

Use console.trace(), breakpoints, or serialize operations to isolate timing issues. Example: Use a mutex library for shared state access.

47. What are the implications of using `global` variables in Node.js modules?

Answer:

global variables cause side effects and collisions across modules. Use module.exports for explicit, isolated state sharing.

48. Why use `Object.seal()` and how is it different from `Object.freeze()`?

Answer:

Object.seal() prevents adding/removing properties but allows value changes; Object.freeze() makes objects fully immutable. Example: Object.seal({ key: 'val' }).

49. How does Node.js buffer incoming HTTP request bodies and when is this problematic?

Answer:

Large request bodies are buffered in memory, risking OOM or DoS. Use streaming with busboy or limit size with express.json({ limit: '10mb' }).

50. What is the effect of `–expose-gc` and when might you use `global.gc()`?

Answer:

--expose-gc enables global.gc() for manual garbage collection, causing pauses. Use for memory testing, not production, due to performance impact.