An HTTP/1.1 client, written from scratch for Node.js
—
HTTP headers management and request/response body handling with multiple content types and streaming support.
WHATWG Headers API implementation for managing HTTP headers with case-insensitive access and validation.
/**
* HTTP headers management
*/
class Headers {
constructor(init?: HeadersInit);
append(name: string, value: string): void;
delete(name: string): void;
get(name: string): string | null;
getSetCookie(): string[];
has(name: string): boolean;
set(name: string, value: string): void;
keys(): IterableIterator<string>;
values(): IterableIterator<string>;
entries(): IterableIterator<[string, string]>;
forEach(callback: (value: string, key: string, parent: Headers) => void, thisArg?: any): void;
[Symbol.iterator](): IterableIterator<[string, string]>;
}
type HeadersInit =
| Headers
| Record<string, string | ReadonlyArray<string>>
| Iterable<readonly [string, string]>;Usage Examples:
import { Headers } from 'undici';
// Create headers from object
const headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
});
// Create headers from array
const headersFromArray = new Headers([
['content-type', 'text/html'],
['cache-control', 'no-cache']
]);
// Manipulate headers
headers.append('accept', 'application/json');
headers.set('user-agent', 'my-app/1.0');
headers.delete('authorization');
// Access headers (case-insensitive)
console.log(headers.get('content-type')); // 'application/json'
console.log(headers.has('Content-Type')); // true
// Iterate headers
for (const [name, value] of headers) {
console.log(`${name}: ${value}`);
}
headers.forEach((value, name) => {
console.log(`${name}: ${value}`);
});
// Handle Set-Cookie headers specially
const responseHeaders = new Headers();
responseHeaders.append('set-cookie', 'session=abc123; Path=/');
responseHeaders.append('set-cookie', 'theme=dark; Path=/');
const setCookies = responseHeaders.getSetCookie();
console.log(setCookies); // ['session=abc123; Path=/', 'theme=dark; Path=/']WHATWG FormData API implementation for handling multipart/form-data and form submissions.
/**
* Form data handling for multipart/form-data
*/
class FormData {
constructor();
append(name: string, value: string | Blob, filename?: string): void;
delete(name: string): void;
get(name: string): FormDataEntryValue | null;
getAll(name: string): FormDataEntryValue[];
has(name: string): boolean;
set(name: string, value: string | Blob, filename?: string): void;
keys(): IterableIterator<string>;
values(): IterableIterator<FormDataEntryValue>;
entries(): IterableIterator<[string, FormDataEntryValue]>;
forEach(callback: (value: FormDataEntryValue, key: string, parent: FormData) => void, thisArg?: any): void;
[Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]>;
}
type FormDataEntryValue = string | File;Usage Examples:
import { FormData, fetch } from 'undici';
import { readFileSync } from 'fs';
// Create form data
const formData = new FormData();
// Add text fields
formData.append('name', 'Alice');
formData.append('email', 'alice@example.com');
formData.append('message', 'Hello world!');
// Add file data
const fileBuffer = readFileSync('./document.pdf');
const file = new Blob([fileBuffer], { type: 'application/pdf' });
formData.append('document', file, 'document.pdf');
// Submit form data
const response = await fetch('https://api.example.com/upload', {
method: 'POST',
body: formData
// Content-Type header is automatically set with boundary
});
// Process existing form data
const existingFormData = new FormData();
existingFormData.append('field1', 'value1');
existingFormData.append('field2', 'value2');
existingFormData.append('field1', 'another value'); // Multiple values
console.log(existingFormData.get('field1')); // 'value1' (first value)
console.log(existingFormData.getAll('field1')); // ['value1', 'another value']
// Iterate form data
for (const [name, value] of existingFormData) {
console.log(`${name}: ${value}`);
}Body handling utilities and types for request and response body processing.
/**
* Body mixin for consuming request/response bodies
*/
interface BodyMixin {
readonly body: ReadableStream | null;
readonly bodyUsed: boolean;
arrayBuffer(): Promise<ArrayBuffer>;
blob(): Promise<Blob>;
bytes(): Promise<Uint8Array>;
json(): Promise<any>;
text(): Promise<string>;
}
type BodyInit =
| ArrayBuffer
| AsyncIterable<Uint8Array>
| Blob
| FormData
| Iterable<Uint8Array>
| NodeJS.ArrayBufferView
| URLSearchParams
| null
| string;
interface BodyReadable extends Readable {
arrayBuffer(): Promise<ArrayBuffer>;
blob(): Promise<Blob>;
bytes(): Promise<Uint8Array>;
json(): Promise<any>;
text(): Promise<string>;
}Usage Examples:
import { fetch, request } from 'undici';
// Process different response body types
const response = await fetch('https://api.example.com/data');
// JSON response
if (response.headers.get('content-type')?.includes('application/json')) {
const data = await response.json();
console.log(data);
}
// Text response
if (response.headers.get('content-type')?.includes('text/')) {
const text = await response.text();
console.log(text);
}
// Binary response
if (response.headers.get('content-type')?.includes('image/')) {
const blob = await response.blob();
const arrayBuffer = await response.arrayBuffer();
const bytes = await response.bytes();
}
// Streaming response body
const streamResponse = await fetch('https://api.example.com/large-data');
const reader = streamResponse.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
// Process chunk
console.log('Received chunk:', value.length, 'bytes');
}
// Using undici.request for body processing
const { body } = await request('https://api.example.com/data');
const jsonData = await body.json();
// Send different body types
await fetch('https://api.example.com/upload', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ message: 'hello' })
});
await fetch('https://api.example.com/upload', {
method: 'POST',
headers: { 'content-type': 'application/octet-stream' },
body: new ArrayBuffer(1024)
});
await fetch('https://api.example.com/upload', {
method: 'POST',
body: new URLSearchParams({ key: 'value', name: 'test' })
});MIME type parsing and serialization utilities for content type handling.
/**
* Parse MIME type strings according to WHATWG spec
* @param input - MIME type string to parse
* @returns Parsed MIME type object or failure
*/
function parseMIMEType(input: string): MimeType | 'failure';
/**
* Serialize MIME type object to string
* @param mimeType - MIME type object to serialize
* @returns Serialized MIME type string
*/
function serializeAMimeType(mimeType: MimeType): string;
interface MimeType {
type: string;
subtype: string;
parameters: Map<string, string>;
essence: string;
}Usage Examples:
import { parseMIMEType, serializeAMimeType } from 'undici';
// Parse MIME types
const mimeType = parseMIMEType('text/html; charset=utf-8');
if (mimeType !== 'failure') {
console.log(mimeType.type); // 'text'
console.log(mimeType.subtype); // 'html'
console.log(mimeType.essence); // 'text/html'
console.log(mimeType.parameters.get('charset')); // 'utf-8'
}
// Parse complex MIME type
const complexType = parseMIMEType('application/json; charset=utf-8; boundary=something');
if (complexType !== 'failure') {
console.log(complexType.parameters.get('boundary')); // 'something'
}
// Serialize MIME type
const serialized = serializeAMimeType({
type: 'application',
subtype: 'json',
parameters: new Map([['charset', 'utf-8']]),
essence: 'application/json'
});
console.log(serialized); // 'application/json; charset=utf-8'
// Handle parsing failures
const invalid = parseMIMEType('invalid-mime-type');
if (invalid === 'failure') {
console.log('Failed to parse MIME type');
}Low-level header parsing utilities for advanced use cases.
/**
* Parse raw headers into structured format
* @param headers - Raw headers to parse
* @returns Parsed headers object
*/
function parseHeaders(headers: Buffer | string): Record<string, string | string[]>;
/**
* Convert header names to canonical string format
* @param headerName - Header name to convert
* @returns Canonical header name string
*/
function headerNameToString(headerName: string | Buffer): string;Usage Examples:
import { parseHeaders, headerNameToString } from 'undici';
// Parse raw headers
const rawHeaders = Buffer.from(
'Content-Type: application/json\r\n' +
'Authorization: Bearer token123\r\n' +
'Set-Cookie: session=abc\r\n' +
'Set-Cookie: theme=dark\r\n'
);
const parsed = parseHeaders(rawHeaders);
console.log(parsed);
// {
// 'content-type': 'application/json',
// 'authorization': 'Bearer token123',
// 'set-cookie': ['session=abc', 'theme=dark']
// }
// Convert header names
console.log(headerNameToString('content-type')); // 'content-type'
console.log(headerNameToString(Buffer.from('AUTHORIZATION'))); // 'authorization'Utilities for parsing and serializing MIME types according to RFC specifications.
/**
* Parse a MIME type string into structured components
* @param input - MIME type string to parse
* @returns Parsed MIME type object or null if invalid
*/
function parseMIMEType(input: string): MIMEType | null;
/**
* Serialize a MIME type object back into a string
* @param mimeType - MIME type object to serialize
* @returns Serialized MIME type string
*/
function serializeAMimeType(mimeType: MIMEType): string;
interface MIMEType {
type: string;
subtype: string;
parameters: Map<string, string>;
}Usage Examples:
import { parseMIMEType, serializeAMimeType } from 'undici';
// Parse MIME type
const mimeType = parseMIMEType('text/html; charset=utf-8; boundary=something');
console.log(mimeType);
// {
// type: 'text',
// subtype: 'html',
// parameters: Map {
// 'charset' => 'utf-8',
// 'boundary' => 'something'
// }
// }
// Create MIME type object
const customMime = {
type: 'application',
subtype: 'json',
parameters: new Map([['charset', 'utf-8']])
};
// Serialize to string
const serialized = serializeAMimeType(customMime);
console.log(serialized); // 'application/json; charset=utf-8'
// Handle invalid MIME types
const invalid = parseMIMEType('not-a-valid-mime-type');
console.log(invalid); // nullinterface File extends Blob {
readonly name: string;
readonly lastModified: number;
}
interface Blob {
readonly size: number;
readonly type: string;
arrayBuffer(): Promise<ArrayBuffer>;
bytes(): Promise<Uint8Array>;
slice(start?: number, end?: number, contentType?: string): Blob;
stream(): ReadableStream<Uint8Array>;
text(): Promise<string>;
}type IncomingHttpHeaders = Record<string, string | string[]>;
type OutgoingHttpHeaders = Record<string, string | string[] | number>;Install with Tessl CLI
npx tessl i tessl/npm-undici