Native fetch socket pool mechanics, Web Crypto vs libuv thread pool, and profiling ReadableStream memory leaks under RPC load.
Module 19 — The Modern Web Standard Shift: Undici, fetch, and Web Crypto
What this module covers: Node.js 18 shipped native
fetch— built on Undici, a from-scratch HTTP/1.1 and HTTP/2 client that replaced the legacyhttp.requestinternals. The connection pool model changed. Garbage collection behavior changed. Performance characteristics changed. Engineers who understandhttp.requestsemantics may be surprised by subtle differences in howfetchhandles keep-alive, connection reuse, and stream consumption. This module covers Undici's architecture, how to avoidReadableStreammemory leaks when consuming large RPC responses, and the Web Crypto API as a replacement for libuv thread-pool-dependent cryptographic operations.
Undici: The New HTTP Client Foundation
undici is the HTTP client library underlying Node.js's native fetch. It was written from scratch (unlike the legacy http module which dates to 2009) with modern HTTP semantics, connection pooling, and proper backpressure.
Connection Pool Architecture
Unlike http.request which uses a per-hostname keep-alive agent, Undici uses a Pool or Client with configurable connection counts:
ReadableStream Memory Leaks: The Critical Mistake
Every fetch response (and Undici response) has a body that is a ReadableStream. If you don't consume the body, the connection is not released back to the pool:
Large Response Streaming
For blockchain RPC responses that return multi-MB block data, streaming avoids loading the entire response into memory:
Performance Profiling: fetch vs http.request
For maximum throughput between internal services: use undici.Pool directly instead of fetch. The fetch API adds overhead for cross-browser compatibility. Pool.request exposes Undici's full performance.
Web Crypto API: Thread-Pool-Free Cryptography
Node.js's crypto module routes expensive operations (PBKDF2, scrypt, key derivation) through the libuv thread pool. The Web Crypto API (globalThis.crypto.subtle) offers the same operations but implemented differently — some operations run synchronously in V8 without thread pool involvement.
Replacing Thread-Pool Operations
Fast Web Crypto Operations (No Thread Pool)
Impact on libuv Thread Pool
Summary
| Concept | Key Takeaway |
|---|---|
| Undici | Underlying fetch in Node.js 18+. Modern connection pool. Better throughput than legacy http. |
Pool.request | Direct Undici API, 71% faster than fetch. Use for inter-service HTTP at high throughput. |
| Body consumption | Always consume the response body. Unconsumed bodies hold connections in the pool forever. |
response.body.cancel() | Explicitly release the connection without reading the body. Use in error paths. |
Web Crypto vs crypto | Web Crypto uses standard API. HMAC, SHA, AES operations often faster than callback-based legacy API. |
Sync crypto.createHash | For hot paths: synchronous SHA-256 via crypto.createHash is fastest. No await overhead. |
crypto.randomUUID() | Synchronous, Web Standards, no thread pool. Best for request ID generation. |
When using Node.js's native fetch (built on Undici), what is a critical consequence of failing to consume the response body (e.g., throwing an error before reading response.json() or response.text())?
For maximum possible HTTP throughput when making internal service-to-service calls, which approach provides the best performance in modern Node.js?
When performing cryptographic operations at very high throughput (e.g., 50,000 tx/sec), what is the main advantage of using crypto.createHash('sha256') over asynchronous APIs like crypto.pbkdf2?
Test your knowledge with more question sets
Sign in to access a wider variety of questions and get notified when new practice sets are added to this module.
Sign in & RegisterDiscussion
0Join the discussion