Recent Posts
Archives

PostHeaderIcon [NodeCongress2024] Deep Dive into Undici: Architecture, Performance, and the Future of HTTP in Node.js

Lecturer: Matteo Collina

Matteo Collina is an internationally recognized expert in Node.js and open-source architecture, serving as the Co-Founder and CTO of Platformatic. He holds a Ph. D. in “Application Platforms for the Internet of Things”. A member of the Node.js Technical Steering Committee (TSC), he is a major contributor to the platform’s core, with a focus on streams, diagnostics, and the HTTP stack. He is the original author of the highly successful, high-performance web framework Fastify and the ultra-fast JSON logger Pino. His open-source modules are downloaded billions of times annually.

Abstract

This article presents a technical analysis of Undici, the high-performance, standards-compliant HTTP/1.1 client that serves as the foundation for the native fetch() API in Node.js. It explains the motivation for Undici’s creation—addressing critical performance and protocol deficiencies in the legacy Node.js stack. The article details the core architectural components, particularly the Client and Dispatcher abstractions, and explains how Undici achieves superior efficiency through advanced connection management and HTTP/1.1 pipelining. The final analysis explores the methodological implications of Undici’s modularity, including enabling zero-overhead internal testing and powering highly efficient modular monolith and microservice runtimes.

Context: Limitations of the Legacy Node.js HTTP Stack

The legacy Node.js HTTP client suffered from several long-standing limitations, primarily in performance and compliance with modern standards. Specifically, it lacked proper support for HTTP/1.1 pipelining—the ability to send multiple requests sequentially over a single connection without waiting for the first response. Furthermore, its connection pool management was inefficient, often failing to enforce proper limits, leading to potential resource exhaustion and performance bottlenecks. Undici was developed to resolve these architectural deficiencies, becoming the native engine for fetch() within Node.js core.

Architecture and Methodology of Undici

Undici’s design is centered around optimizing connection usage and abstracting the request lifecycle:

  • The Client and Connection Pools: The core component is the Client, which is scoped to a single origin (protocol, hostname, and port). The Client manages a pool of TCP connections and is responsible for implementing the efficiency of the HTTP protocol.
  • Pipelining for Performance: Undici explicitly implements HTTP/1.1 pipelining. This methodology permits the efficient use of the network and is essential for maximum HTTP/1.1 performance, particularly when connecting to modern servers that support the feature.
  • The Dispatcher Abstraction: Undici utilizes a pluggable Dispatcher interface. This abstraction governs the process of taking a request, managing connection logic, and writing the request to a socket. Key Dispatcher implementations include the standard Client (for a single origin) and the Agent (for multiple origins).
  • Connection Management: The pooling mechanism employs a strategy to retire connections gracefully to allow DNS changes and resource rotation, contrasting with legacy systems that often held connections indefinitely.

Consequences and Architectural Innovations

Undici’s modular and abstracted architecture has led to significant innovations beyond core HTTP performance:

  1. In-Process Request Testing: The Dispatcher model allows for the implementation of a MockClient (accessible via the light-my-request module), which completely bypasses the network stack. This permits the injection of HTTP requests directly into a running Node.js server within the same process, enabling zero-overhead, high-speed unit and integration testing without opening any actual sockets.
  2. Internal Mesh Networking: The architecture enables a unique pattern for running multiple microservices within a single process. Using a custom dispatcher (fastify-undici-dispatcher), internal HTTP requests can be routed directly to other services (e.g., Fastify instances) running in the same process via an in-memory mesh network, completely bypassing the network layer for inter-service communication. This methodology, employed in the Platformatic runtime, allows developers to transition from a modular monolith to a microservice architecture with minimal code changes, retaining maximum performance for inter-service calls.

Links

Hashtags: #Undici #NodeJS #HTTPClient #Fastify #Microservices #PerformanceEngineering #Platformatic

Leave a Reply