// libuv's thread pool size can be tuned via an environment variable
process.env.UV_THREADPOOL_SIZE = 8; // must be set before any threadpool-using operation runs
Topics
40
Advanced Node.js
Architecture & Design Patterns
Async Patterns
Authentication & Authorization
Authentication & Authorization (JWT, OAuth, Passport)
Background Jobs & Queues
Caching
Caching with Redis
Child Processes & Process Management
CLI Tools & Scripting with Node.js
Cloud & DevOps
Clustering & Worker Threads
Core Node.js Modules
Databases
Databases & ORMs (MongoDB/Mongoose, SQL/Sequelize)
Debugging & Diagnostics
Deployment & Process Managers (PM2)
Docker & Containerization for Node.js
Docker & Deployment
Email & Notifications
Environment Variables & Configuration
Error Handling
Event Loop & Non-blocking IO
Events & EventEmitter
Express & Middleware
File System & File Processing
File System (fs) Module
File Uploads & Media Processing
Git & Project Management
Global Objects & the process Object
GraphQL
GraphQL with Node.js
HTTP & HTTPS Modules
HTTP & Web Servers
Logging & Monitoring
Message Queues (RabbitMQ & Kafka)
Microservices Architecture with Node.js
Node.js Fundamentals & Runtime Architecture
Path & OS Modules
Performance Optimization & Profiling
Advanced Node.js
15 questions found
libuv is the C library underlying Node.js that provides the event loop, thread pool, and asynchronous I/O abstractions across operating systems -- it handles file system operations, DNS lookups, and networking by delegating blocking work to a small pool of background threads (default size 4) while keeping the main JavaScript thread free to keep executing.
Real-world example
A file-processing service handling many concurrent fs.readFile calls raises UV_THREADPOOL_SIZE from the default 4 to 16 to reduce queuing delay, since file I/O on that platform is routed through libuv's thread pool rather than being truly non-blocking at the OS level.
Event Loop & Non-blocking IO;Performance Optimization & Profiling
How do you use the --inspect flag and Chrome DevTools to profile a running Node.js application in production?
AdvancedRunning node --inspect (or --inspect-brk to pause on the first line) opens a WebSocket debugging port that Chrome DevTools or VS Code can attach to, exposing the CPU profiler, heap snapshots, and live breakpoints against the running process. In production, --inspect should be bound to localhost only (never a public interface) and typically accessed through an SSH tunnel, since anyone who can connect to the inspector port gets full remote code execution in that process.
node --inspect=127.0.0.1:9229 server.js
// then open chrome://inspect in Chrome, or connect VS Code's Node debugger
// SSH tunnel from a local machine to a remote server:
// ssh -L 9229:localhost:9229 user@remote-host
Real-world example
An intermittent memory leak in a production API is diagnosed by attaching Chrome DevTools via an SSH-tunneled --inspect session, taking two heap snapshots ten minutes apart, and comparing retained object counts to identify a growing array of unclosed database connections.
Debugging & Diagnostics;Performance Optimization & Profiling
What is the difference between process.nextTick(), setImmediate(), and setTimeout(fn, 0) in terms of execution order?
Advancedprocess.nextTick() queues a callback to run immediately after the current operation completes, before the event loop continues to any phase -- it has the highest priority and can starve I/O if called recursively. setImmediate() queues a callback to run in the check phase of the event loop, after I/O callbacks in the current iteration. setTimeout(fn, 0) queues a callback for the timers phase, and its exact ordering relative to setImmediate() from the main module is not guaranteed, though inside an I/O callback setImmediate() always fires first.
console.log('start');
setTimeout(() => console.log('setTimeout'), 0);
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick'));
console.log('end');
// Output: start, end, nextTick, then setTimeout/setImmediate (order can vary at top level)
Real-world example
A library author uses process.nextTick() to guarantee that a callback passed synchronously to a constructor is still always invoked asynchronously (never synchronously), preserving a predictable execution contract for consumers regardless of whether the underlying operation happens to resolve instantly.
Event Loop & Non-blocking IO;Async Patterns
A diagnostic report (enabled via --report-uncaught-exception, --report-on-signal, or triggered programmatically) is a JSON snapshot of the process's state at a point in time -- including JavaScript and native stack traces, heap statistics, loaded modules, environment variables, and resource usage -- useful for post-mortem analysis of crashes or hangs in production without needing to reproduce the issue.
// Trigger a report on demand from within the process
const { writeReport } = require('node:process').report;
writeReport('report.json');
// Or generate one automatically on uncaught exceptions
// node --report-uncaught-exception --report-on-fatalerror app.js
Real-world example
A production service that occasionally hangs under load is configured with --report-on-signal so operators can send SIGUSR2 to the process during a hang, capturing a full diagnostic report for offline analysis without restarting or losing the in-progress state.
Debugging & Diagnostics;Error Handling
What is V8's hidden class mechanism, and why does it matter for writing performant Node.js code?
AdvancedV8 optimizes object property access by internally assigning objects a 'hidden class' based on the shape (set and order of properties) of the object -- objects sharing the same shape share the same hidden class, letting V8 generate fast, monomorphic machine code for property access. Adding properties in inconsistent orders, deleting properties, or changing a property's type after creation causes V8 to create new hidden classes and fall back to slower lookup, which is why consistently initializing all of an object's properties in the constructor (even to placeholder values) is a common performance practice.
// Bad: inconsistent shapes hurt V8's optimization
function Point(x, y) { this.x = x; if (y) this.y = y; }
// Good: consistent shape every time, same hidden class
function Point(x, y) { this.x = x; this.y = y || 0; }
Real-world example
A high-throughput data-processing pipeline sees a measurable speedup after refactoring to always initialize every object field (even to null placeholders) in the same order, letting V8 keep using a single stable hidden class instead of repeatedly deoptimizing.
Performance Optimization & Profiling;Memory Management & Garbage Collection
What is backpressure in Node.js streams, and how do you handle it correctly when piping data?
AdvancedBackpressure occurs when a writable destination can't consume data as fast as a readable source produces it -- if ignored, unbounded data buffers in memory and can crash the process. Node signals this through the return value of stream.write(), which returns false when the internal buffer has exceeded its highWaterMark; correctly written code pauses the source (or awaits a 'drain' event) until the writable is ready again, which is exactly what .pipe() and pipeline() do automatically.
const canContinue = writable.write(chunk);
if (!canContinue) {
readable.pause();
writable.once('drain', () => readable.resume());
}
// Or, preferably, let pipeline() manage this automatically:
const { pipeline } = require('node:stream/promises');
await pipeline(readable, writable);
Real-world example
A log-shipping service piping a large file to a slow network socket avoids an out-of-memory crash by using stream.pipeline() instead of manual .on('data') handlers, letting Node's built-in backpressure handling automatically throttle file reads to match the socket's actual write speed.
Streams & Buffers;Performance Optimization & Profiling
What is the AsyncLocalStorage API, and what problem does it solve for tracking context across asynchronous calls?
AdvancedAsyncLocalStorage (from node:async_hooks) lets you store and retrieve data that stays associated with a single asynchronous execution chain (like one HTTP request) without needing to explicitly pass that data through every function call in between -- it's commonly used to propagate a request ID or user context through logging and downstream calls, replacing older, more fragile approaches to context propagation.
const { AsyncLocalStorage } = require('node:async_hooks');
const als = new AsyncLocalStorage();
app.use((req, res, next) => {
als.run({ requestId: crypto.randomUUID() }, next);
});
function log(message) {
const store = als.getStore();
console.log(`[${store?.requestId}] ${message}`);
}
Real-world example
A microservice attaches a unique request ID to every incoming request using AsyncLocalStorage, so that every log line emitted anywhere in that request's async call chain -- database queries, downstream API calls, error handlers -- automatically includes the same request ID without threading it through every function signature.
Async Patterns;Logging & Monitoring
How does Node.js implement the Worker Threads module differently from Child Processes, and when should each be used?
AdvancedWorker threads (node:worker_threads) run genuine OS threads within the same process, sharing memory via SharedArrayBuffer and communicating cheaply via structured-clone message passing -- ideal for CPU-bound work like image processing or complex calculations that would otherwise block the event loop. Child processes (node:child_process) spawn entirely separate OS processes with isolated memory, heavier startup cost, and communication only via serialized IPC or stdio -- better suited for running external programs or isolating untrusted/crash-prone code.
const { Worker } = require('node:worker_threads');
const worker = new Worker('./cpu-intensive-task.js', { workerData: { n: 40 } });
worker.on('message', (result) => console.log('Fibonacci result:', result));
// vs. child_process for running a separate program
const { spawn } = require('node:child_process');
spawn('python3', ['script.py']);
Real-world example
An image-resizing API offloads the actual pixel-processing work to a pool of worker threads so the main event loop stays free to accept new HTTP requests, while a separate video-transcoding service uses child_process.spawn to run ffmpeg as an isolated external process instead.
Clustering & Worker Threads;Child Processes & Process Management
What is monkey-patching in the context of Node.js APM (Application Performance Monitoring) tools, and what are its risks?
AdvancedAPM tools like New Relic or Datadog commonly instrument an application by monkey-patching -- overriding core module methods (like http.request or a database driver's query method) at runtime to inject timing and tracing logic transparently, without requiring the application code itself to change. The risk is that patched functions can subtly change behavior (altering stack traces, breaking assumptions about function.length or this binding), and multiple APM/tracing libraries patching the same method can conflict, causing hard-to-diagnose bugs.
// Simplified monkey-patch example
const originalRequest = http.request;
http.request = function(...args) {
const start = Date.now();
const req = originalRequest.apply(this, args);
req.on('response', () => console.log('Request took', Date.now() - start, 'ms'));
return req;
};
Real-world example
A team debugging an intermittent production issue eventually traces it to two separately installed tracing libraries both monkey-patching the same database driver method, each wrapping the other's wrapper and silently swallowing an error that should have propagated to the application's error handler.
Logging & Monitoring;Performance Optimization & Profiling
What is the node:diagnostics_channel module, and how does it differ from simply emitting a custom event?
Advanceddiagnostics_channel provides a standardized, low-overhead publish/subscribe mechanism specifically designed for diagnostics and instrumentation data -- unlike a regular EventEmitter, publishing to a channel with no active subscribers has near-zero cost (the message object is never even constructed), making it safe to instrument hot code paths without a performance penalty when no one is listening. Node's own core modules (like HTTP and the built-in test runner) publish diagnostic events through named channels that APM and tracing tools can subscribe to.
const diagnostics_channel = require('node:diagnostics_channel');
const channel = diagnostics_channel.channel('my-app:db-query');
if (channel.hasSubscribers) {
channel.publish({ query: sql, duration: elapsedMs });
}
// Elsewhere, a monitoring tool subscribes:
diagnostics_channel.subscribe('my-app:db-query', (message) => {
console.log('Query took', message.duration, 'ms');
});
Real-world example
A database library publishes query timing data on a diagnostics_channel rather than emitting a custom event, so that when no monitoring tool is subscribed the library incurs virtually no overhead in production, but a team can opt into detailed query tracing simply by subscribing when they need to debug performance.
Logging & Monitoring;Event Loop & Non-blocking IO
Showing 1–10 of 15