Advanced Node.js

15 questions found

How do you detect and prevent event loop blocking in a long-running Node.js service?

Advanced
Event loop blocking happens when synchronous code (a large JSON.parse, a tight computational loop, a synchronous crypto operation) runs long enough to delay all other pending callbacks, timers, and I/O -- detectable via tools like the loopbench or blocked-at packages, which measure the delay between when a timer should fire and when it actually does. Prevention means offloading CPU-heavy work to worker threads, breaking large synchronous loops into chunks processed across multiple event loop ticks, and using streaming (rather than loading entire payloads into memory) for large data.
const blocked = require('blocked-at');
blocked((time, stack) => {
  console.log(`Blocked for ${time}ms`, stack);
}, { threshold: 100 });

// Chunking a large synchronous loop across ticks
function processInChunks(items, i = 0) {
  const end = Math.min(i + 1000, items.length);
  for (; i < end; i++) { /* process items[i] */ }
  if (i < items.length) setImmediate(() => processInChunks(items, i));
}
Real-world example An API that occasionally froze for several seconds under load is traced, using blocked-at, to a synchronous bcrypt.hashSync() call on the main thread; switching to the async bcrypt.hash() variant (which offloads to the libuv thread pool) eliminates the freezes entirely.

Common follow-ups: What's the practical difference between offloading work to a worker thread versus just chunking it across setImmediate() calls?;How would you set up automated alerting for event loop lag in production?

Event Loop & Non-blocking IO;Clustering & Worker Threads

What are Node.js's experimental permission model flags, and what problem do they address?

Advanced
Introduced behind the --experimental-permission flag, Node's permission model lets you restrict what a running process is allowed to do -- for example, denying file system access outside specific directories, or blocking the ability to spawn child processes -- at the process level rather than relying purely on OS-level sandboxing or third-party libraries. This addresses supply-chain risk: if a compromised or malicious npm dependency tries to read arbitrary files or make unexpected network calls, a properly configured permission model can block it outright.
# Only allow file system read access to a specific directory
node --experimental-permission --allow-fs-read=/app/data server.js

# Deny child process spawning entirely
node --experimental-permission --allow-child-process=false server.js
Real-world example A company running third-party plugin code inside their Node.js platform enables the permission model to restrict plugins to read-only access within a designated sandbox directory, containing the blast radius if a malicious or buggy plugin is ever installed.

Common follow-ups: How mature and production-ready is this feature as of the current LTS release, given it's still experimental?;How does this compare to using a full OS-level sandbox like a container or a VM?

Security;Child Processes & Process Management

How does Node.js's built-in test runner (node:test) compare to third-party frameworks like Jest or Mocha?

Advanced
Node's built-in test runner (stable since Node 20, available via node:test) provides test suites, hooks (before/after), mocking, code coverage, and a TAP-compatible reporter without any external dependency -- reducing install size and avoiding version-compatibility churn. It lacks some of Jest's more advanced features out of the box (like built-in snapshot testing or extensive matcher libraries), but for many projects it's now sufficient on its own, and can be paired with assert or a lightweight assertion library.
const { test } = require('node:test');
const assert = require('node:assert');

test('adds two numbers', () => {
  assert.strictEqual(1 + 1, 2);
});

// Run with: node --test
// Or with coverage: node --test --experimental-test-coverage
Real-world example A small internal CLI tool drops its Jest dependency entirely in favor of node:test, since the project has minimal testing needs and removing Jest cuts the node_modules install size significantly and eliminates a whole category of dependency-version conflicts.

Common follow-ups: What testing features does Jest still provide that node:test currently lacks?;How would you migrate an existing Jest test suite to node:test incrementally?

Testing with Jest Mocha & the Node Test Runner;CLI Tools & Scripting with Node.js

What is a memory leak in the context of a long-running Node.js server, and what are the most common causes?

Advanced
A memory leak occurs when objects that are no longer needed remain reachable from a GC root, preventing the garbage collector from reclaiming them, causing memory usage to climb steadily over time until the process crashes or is killed. Common causes in Node.js include: event listeners added repeatedly without removal (especially on long-lived EventEmitters), closures capturing large objects unintentionally, unbounded caches or arrays that grow without eviction, and timers/intervals that are never cleared.
// Leak: a new listener is added on every request, never removed
app.get('/data', (req, res) => {
  eventEmitter.on('update', () => res.json(getData())); // accumulates forever
});

// Fixed: use .once(), or explicitly remove the listener afterward
app.get('/data', (req, res) => {
  eventEmitter.once('update', () => res.json(getData()));
});
Real-world example A service's memory grows steadily over several days until it's killed by the OS; heap snapshot comparison in Chrome DevTools reveals thousands of accumulated 'update' listeners on a singleton EventEmitter, traced back to a route handler that added a new listener on every request without ever removing it.

Common follow-ups: How do you use Node's --max-old-space-size flag as a mitigation versus actually fixing the root cause?;What tool-based workflow would you use to systematically diagnose a suspected leak in production?

Memory Management & Garbage Collection;Debugging & Diagnostics

What are Node.js Single Executable Applications (SEA), and what are their current limitations?

Advanced
Single Executable Applications, stable since Node 20+, let you package a Node.js application and its dependencies into a single standalone binary that runs without requiring a separate Node.js installation on the target machine -- built by injecting a compiled JavaScript blob into a copy of the Node binary itself. Current limitations include no built-in support for native addons in every configuration, larger resulting binary sizes than lightweight alternatives, and less mature tooling for cross-compiling for other platforms compared to tools purpose-built for this (like pkg or nexe historically).
# Generate a config and blob
node --experimental-sea-config sea-config.json

# Inject the blob into a copy of the node binary
node -e "require('fs').copyFileSync(process.execPath, 'myapp')"
postject myapp NODE_SEA_BLOB sea-prep.blob \
  --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2
Real-world example A CLI tool vendor ships their Node.js-based utility as a single executable using SEA so that end users on machines without Node.js installed can simply download and run one file, avoiding the friction of requiring them to install a Node.js runtime first.

Common follow-ups: How does SEA compare in practice to using Docker as the distribution mechanism instead?;What specific native modules or dependencies are currently known to be incompatible with SEA packaging?

CLI Tools & Scripting with Node.js;Deployment & Process Managers (PM2)

Showing 11–15 of 15