Undici
An HTTP/1.1 client, written from scratch for Node.js
Install / Use
/learn @nodejs/UndiciREADME
undici
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.requestfor 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:
undici.request()- Fastest, most efficientundici.fetch()- Good performance, standard compliance- 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-ControlandExpiresheaders - Validation: Supports
ETagandLast-Modifiedvalidation - 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
