SkillAgentSearch skills...

Undici

An HTTP/1.1 client, written from scratch for Node.js

Install / Use

/learn @nodejs/Undici
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

undici

Node CI neostandard javascript style npm version codecov

An HTTP/1.1 client, written from scratch for Node.js.

Undici means eleven in Italian. 1.1 -> 11 -> Eleven -> Undici. It is also a Stranger Things reference.

How to get involved

Have a question about using Undici? Open a Q&A Discussion or join our official OpenJS Slack channel.

Looking to contribute? Start by reading the contributing guide

Install

npm i undici

Benchmarks

The benchmark is a simple getting data example using a 50 TCP connections with a pipelining depth of 10 running on Node 22.11.0.

┌────────────────────────┬─────────┬────────────────────┬────────────┬─────────────────────────┐
│  Tests                 │ Samples │ Result             │ Tolerance  │ Difference with slowest │
├────────────────────────┼─────────┼────────────────────┼────────────┼─────────────────────────┤
│  'axios'               │ 15      │ '5708.26 req/sec'  │ '± 2.91 %' │ '-'                     │
│  'http - no keepalive' │ 10      │ '5809.80 req/sec'  │ '± 2.30 %' │ '+ 1.78 %'              │
│  'request'             │ 30      │ '5828.80 req/sec'  │ '± 2.91 %' │ '+ 2.11 %'              │
│  'undici - fetch'      │ 40      │ '5903.78 req/sec'  │ '± 2.87 %' │ '+ 3.43 %'              │
│  'node-fetch'          │ 10      │ '5945.40 req/sec'  │ '± 2.13 %' │ '+ 4.15 %'              │
│  'got'                 │ 35      │ '6511.45 req/sec'  │ '± 2.84 %' │ '+ 14.07 %'             │
│  'http - keepalive'    │ 65      │ '9193.24 req/sec'  │ '± 2.92 %' │ '+ 61.05 %'             │
│  'superagent'          │ 35      │ '9339.43 req/sec'  │ '± 2.95 %' │ '+ 63.61 %'             │
│  'undici - pipeline'   │ 50      │ '13364.62 req/sec' │ '± 2.93 %' │ '+ 134.13 %'            │
│  'undici - stream'     │ 95      │ '18245.36 req/sec' │ '± 2.99 %' │ '+ 219.63 %'            │
│  'undici - request'    │ 50      │ '18340.17 req/sec' │ '± 2.84 %' │ '+ 221.29 %'            │
│  'undici - dispatch'   │ 40      │ '22234.42 req/sec' │ '± 2.94 %' │ '+ 289.51 %'            │
└────────────────────────┴─────────┴────────────────────┴────────────┴─────────────────────────┘

Undici vs. Fetch

Overview

Node.js includes a built-in fetch() implementation powered by undici starting from Node.js v18. However, there are important differences between using the built-in fetch and installing undici as a separate module.

Built-in Fetch (Node.js v18+)

Node.js's built-in fetch is powered by a bundled version of undici:

// Available globally in Node.js v18+
const response = await fetch('https://api.example.com/data');
const data = await response.json();

// Check the bundled undici version
console.log(process.versions.undici); // e.g., "5.28.4"

Pros:

  • No additional dependencies required
  • Works across different JavaScript runtimes
  • Automatic compression handling (gzip, deflate, br)
  • Built-in caching support (in development)

Cons:

  • Limited to the undici version bundled with your Node.js version
  • Less control over connection pooling and advanced features
  • Error handling follows Web API standards (errors wrapped in TypeError)
  • Performance overhead due to Web Streams implementation

Undici Module

Installing undici as a separate module gives you access to the latest features and APIs:

npm install undici
import { request, fetch, Agent, setGlobalDispatcher } from 'undici';

// Use undici.request for maximum performance
const { statusCode, headers, body } = await request('https://api.example.com/data');
const data = await body.json();

// Or use undici.fetch with custom configuration
const agent = new Agent({ keepAliveTimeout: 10000 });
setGlobalDispatcher(agent);
const response = await fetch('https://api.example.com/data');

Pros:

  • Latest undici features and bug fixes
  • Access to advanced APIs (request, stream, pipeline)
  • Fine-grained control over connection pooling
  • Better error handling with clearer error messages
  • Superior performance, especially with undici.request
  • HTTP/1.1 pipelining support
  • Custom interceptors and middleware
  • Advanced features like ProxyAgent, Socks5Agent, MockAgent

Cons:

  • Additional dependency to manage
  • Larger bundle size

When to Use Each

Use Built-in Fetch When:

  • You want zero dependencies
  • Building isomorphic code that runs in browsers and Node.js
  • Publishing to npm and want to maximize compatibility with JS runtimes
  • Simple HTTP requests without advanced configuration
  • You're publishing to npm and you want to maximize compatiblity
  • You don't depend on features from a specific version of undici

Use Undici Module When:

  • You need the latest undici features and performance improvements
  • You require advanced connection pooling configuration
  • You need APIs not available in the built-in fetch (ProxyAgent, Socks5Agent, MockAgent, etc.)
  • Performance is critical (use undici.request for maximum speed)
  • You want better error handling and debugging capabilities
  • You need HTTP/1.1 pipelining or advanced interceptors
  • You prefer decoupled protocol and API interfaces

Performance Comparison

Based on benchmarks, here's the typical performance hierarchy:

  1. undici.request() - Fastest, most efficient
  2. undici.fetch() - Good performance, standard compliance
  3. Node.js http/https - Baseline performance

Migration Guide

If you're currently using built-in fetch and want to migrate to undici:

// Before: Built-in fetch
const response = await fetch('https://api.example.com/data');

// After: Undici fetch (drop-in replacement)
import { fetch } from 'undici';
const response = await fetch('https://api.example.com/data');

// Or: Undici request (better performance)
import { request } from 'undici';
const { statusCode, body } = await request('https://api.example.com/data');
const data = await body.json();

Keep fetch and FormData together

When you send a FormData body, keep fetch and FormData from the same implementation.

Use one of these patterns:

// Built-in globals
const body = new FormData()
body.set('name', 'some')
await fetch('https://example.com', {
  method: 'POST',
  body
})
// undici module imports
import { fetch, FormData } from 'undici'

const body = new FormData()
body.set('name', 'some')
await fetch('https://example.com', {
  method: 'POST',
  body
})

If you want the installed undici package to provide the globals, call install() first:

import { install } from 'undici'

install()

const body = new FormData()
body.set('name', 'some')
await fetch('https://example.com', {
  method: 'POST',
  body
})

install() replaces the global fetch, Headers, Response, Request, and FormData implementations with undici's versions, so they all match.

Avoid mixing a global FormData with undici.fetch(), or undici.FormData with the built-in global fetch().

Version Compatibility

You can check which version of undici is bundled with your Node.js version:

console.log(process.versions.undici);

Installing undici as a module allows you to use a newer version than what's bundled with Node.js, giving you access to the latest features and performance improvements.

Quick Start

Basic Request

import { request } from 'undici'

const {
  statusCode,
  headers,
  trailers,
  body
} = await request('http://localhost:3000/foo')

console.log('response received', statusCode)
console.log('headers', headers)

for await (const data of body) { console.log('data', data) }

console.log('trailers', trailers)

Using Cache Interceptor

Undici provides a powerful HTTP caching interceptor that follows HTTP caching best practices. Here's how to use it:

import { fetch, Agent, interceptors, cacheStores } from 'undici';

// Create a client with cache interceptor
const client = new Agent().compose(interceptors.cache({
  // Optional: Configure cache store (defaults to MemoryCacheStore)
  store: new cacheStores.MemoryCacheStore({
    maxSize: 100 * 1024 * 1024, // 100MB
    maxCount: 1000,
    maxEntrySize: 5 * 1024 * 1024 // 5MB
  }),
  
  // Optional: Specify which HTTP methods to cache (default: ['GET', 'HEAD'])
  methods: ['GET', 'HEAD']
}));

// Set the global dispatcher to use our caching client
setGlobalDispatcher(client);

// Now all fetch requests will use the cache
async function getData() {
  const response = await fetch('https://api.example.com/data');
  // The server should set appropriate Cache-Control headers in the response
  // which the cache will respect based on the cache policy
  return response.json();
}

// First request - fetches from origin
const data1 = await getData();

// Second request - served from cache if within max-age
const data2 = await getData();

Key Features:

  • Automatic Caching: Respects Cache-Control and Expires headers
  • Validation: Supports ETag and Last-Modified validation
  • Storage Options: In-memory or persistent SQLite storage
  • Flexible: Configure cache size, TTL, and more

Global Installation

Undici provides an install() function to add all WHATWG fetch classes to globalThis, making them available globally:

import { install } from 'undici'

// Install all WHATWG fetch clas
View on GitHub
GitHub Stars7.5k
CategoryDevelopment
Updated3h ago
Forks742

Languages

JavaScript

Security Score

100/100

Audited on Apr 3, 2026

No findings