A stand-alone types package for Undici HTTP client library
—
Web-compatible implementations of fetch, WebSocket, FormData and related APIs that follow standard web specifications. These APIs provide familiar interfaces for developers coming from browser environments.
Standards-compliant fetch implementation for HTTP requests.
/**
* Perform HTTP requests using the standard web fetch API
* @param input - URL string or Request object
* @param init - Request configuration options
* @returns Promise resolving to Response object
*/
function fetch(
input: RequestInfo,
init?: RequestInit
): Promise<Response>;
type RequestInfo = Request | string | URL;
interface RequestInit {
method?: string;
headers?: HeadersInit;
body?: BodyInit;
mode?: RequestMode;
credentials?: RequestCredentials;
cache?: RequestCache;
redirect?: RequestRedirect;
referrer?: string;
referrerPolicy?: ReferrerPolicy;
integrity?: string;
keepalive?: boolean;
signal?: AbortSignal;
window?: null;
duplex?: RequestDuplex;
}
type RequestMode = "cors" | "no-cors" | "same-origin" | "navigate";
type RequestCredentials = "omit" | "same-origin" | "include";
type RequestCache = "default" | "no-store" | "reload" | "no-cache" | "force-cache" | "only-if-cached";
type RequestRedirect = "follow" | "error" | "manual";
type RequestDuplex = "half";Usage Examples:
import { fetch } from "undici-types";
// Simple GET request
const response = await fetch("https://api.example.com/users");
const users = await response.json();
// POST request with JSON
const response = await fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
name: "John Doe",
email: "john@example.com"
})
});
// Request with AbortController
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch("https://api.example.com/slow", {
signal: controller.signal
});
} catch (error) {
if (error.name === 'AbortError') {
console.log('Request was aborted');
}
}Represents an HTTP request with full configuration options.
/**
* Represents an HTTP request
*/
class Request implements BodyMixin {
constructor(input: RequestInfo, init?: RequestInit);
readonly method: string;
readonly url: string;
readonly headers: Headers;
readonly body: ReadableStream<Uint8Array> | null;
readonly bodyUsed: boolean;
readonly cache: RequestCache;
readonly credentials: RequestCredentials;
readonly destination: RequestDestination;
readonly integrity: string;
readonly keepalive: boolean;
readonly mode: RequestMode;
readonly redirect: RequestRedirect;
readonly referrer: string;
readonly referrerPolicy: ReferrerPolicy;
readonly signal: AbortSignal;
clone(): Request;
// BodyMixin methods
arrayBuffer(): Promise<ArrayBuffer>;
blob(): Promise<Blob>;
formData(): Promise<FormData>;
json(): Promise<any>;
text(): Promise<string>;
}
type RequestDestination = "" | "audio" | "audioworklet" | "document" | "embed" | "font" | "frame" | "iframe" | "image" | "manifest" | "object" | "paintworklet" | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track" | "video" | "worker" | "xslt";Usage Examples:
import { Request, fetch } from "undici-types";
// Create reusable request
const apiRequest = new Request("https://api.example.com/data", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer token123"
},
body: JSON.stringify({ query: "users" })
});
// Use request multiple times
const response1 = await fetch(apiRequest.clone());
const response2 = await fetch(apiRequest.clone());
// Access request properties
console.log(apiRequest.method); // "POST"
console.log(apiRequest.url); // "https://api.example.com/data"
console.log(apiRequest.headers.get("Content-Type")); // "application/json"Represents an HTTP response with body parsing methods.
/**
* Represents an HTTP response
*/
class Response implements BodyMixin {
constructor(body?: BodyInit | null, init?: ResponseInit);
readonly type: ResponseType;
readonly url: string;
readonly redirected: boolean;
readonly status: number;
readonly ok: boolean;
readonly statusText: string;
readonly headers: Headers;
readonly body: ReadableStream<Uint8Array> | null;
readonly bodyUsed: boolean;
clone(): Response;
// BodyMixin methods
arrayBuffer(): Promise<ArrayBuffer>;
blob(): Promise<Blob>;
formData(): Promise<FormData>;
json(): Promise<any>;
text(): Promise<string>;
// Static methods
static error(): Response;
static json(data: any, init?: ResponseInit): Response;
static redirect(url: string | URL, status?: ResponseRedirectStatus): Response;
}
interface ResponseInit {
status?: number;
statusText?: string;
headers?: HeadersInit;
}
type ResponseType = "basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect";
type ResponseRedirectStatus = 300 | 301 | 302 | 303 | 307 | 308;Usage Examples:
import { fetch, Response } from "undici-types";
// Handle response
const response = await fetch("https://api.example.com/users");
if (response.ok) {
const contentType = response.headers.get("content-type");
if (contentType?.includes("application/json")) {
const data = await response.json();
console.log(data);
} else {
const text = await response.text();
console.log(text);
}
} else {
console.error(`Error: ${response.status} ${response.statusText}`);
}
// Create custom responses
const jsonResponse = Response.json({ message: "Hello" }, {
status: 200,
headers: { "Custom-Header": "value" }
});
const redirectResponse = Response.redirect("https://example.com/new-location", 301);HTTP headers manipulation following web standards.
/**
* HTTP headers collection with case-insensitive access
*/
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;
forEach(callback: (value: string, key: string, parent: Headers) => void, thisArg?: any): void;
keys(): IterableIterator<string>;
values(): IterableIterator<string>;
entries(): IterableIterator<[string, string]>;
[Symbol.iterator](): IterableIterator<[string, string]>;
}
type HeadersInit = Headers | Record<string, string | string[]> | Iterable<readonly [string, string]>;Usage Examples:
import { Headers, fetch } from "undici-types";
// Create headers object
const headers = new Headers();
headers.set("Content-Type", "application/json");
headers.set("Authorization", "Bearer token123");
headers.append("Accept", "application/json");
// Use with fetch
const response = await fetch("https://api.example.com/data", {
method: "POST",
headers: headers,
body: JSON.stringify({ data: "example" })
});
// Iterate headers
for (const [name, value] of headers) {
console.log(`${name}: ${value}`);
}
// Create from object
const headers2 = new Headers({
"Content-Type": "text/plain",
"Custom-Header": "custom-value"
});
// Create from array
const headers3 = new Headers([
["Content-Type", "application/xml"],
["Accept", "application/xml"]
]);WebSocket client implementation following web standards.
/**
* WebSocket client implementation
*/
class WebSocket extends EventTarget {
constructor(url: string | URL, protocols?: string | string[], options?: WebSocketInit);
static readonly CONNECTING: 0;
static readonly OPEN: 1;
static readonly CLOSING: 2;
static readonly CLOSED: 3;
readonly CONNECTING: 0;
readonly OPEN: 1;
readonly CLOSING: 2;
readonly CLOSED: 3;
readonly url: string;
readonly readyState: number;
readonly extensions: string;
readonly protocol: string;
binaryType: BinaryType;
onopen: ((this: WebSocket, ev: Event) => any) | null;
onmessage: ((this: WebSocket, ev: MessageEvent) => any) | null;
onerror: ((this: WebSocket, ev: ErrorEvent) => any) | null;
onclose: ((this: WebSocket, ev: CloseEvent) => any) | null;
send(data: string | ArrayBuffer | ArrayBufferView | Blob): void;
close(code?: number, reason?: string): void;
addEventListener<K extends keyof WebSocketEventMap>(
type: K,
listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
options?: boolean | AddEventListenerOptions
): void;
removeEventListener<K extends keyof WebSocketEventMap>(
type: K,
listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
options?: boolean | EventListenerOptions
): void;
}
interface WebSocketInit {
protocols?: string | string[];
dispatcher?: Dispatcher;
headers?: HeadersInit;
followRedirects?: boolean;
}
type BinaryType = "blob" | "arraybuffer";
interface WebSocketEventMap {
"close": CloseEvent;
"error": ErrorEvent;
"message": MessageEvent;
"open": Event;
}
interface CloseEvent extends Event {
readonly code: number;
readonly reason: string;
readonly wasClean: boolean;
}
interface MessageEvent extends Event {
readonly data: any;
readonly lastEventId: string;
readonly origin: string;
readonly ports: readonly MessagePort[];
readonly source: MessageEventSource | null;
}
interface ErrorEvent extends Event {
readonly message: string;
readonly filename: string;
readonly lineno: number;
readonly colno: number;
readonly error: any;
}Usage Examples:
import { WebSocket } from "undici-types";
// Basic WebSocket connection
const ws = new WebSocket("ws://localhost:8080");
ws.onopen = (event) => {
console.log("WebSocket connected");
ws.send("Hello Server!");
};
ws.onmessage = (event) => {
console.log("Received:", event.data);
};
ws.onclose = (event) => {
console.log(`WebSocket closed: ${event.code} ${event.reason}`);
};
ws.onerror = (event) => {
console.error("WebSocket error:", event.error);
};
// WebSocket with protocols and headers
const ws2 = new WebSocket("wss://api.example.com/ws", ["protocol1", "protocol2"], {
headers: {
"Authorization": "Bearer token123"
}
});
// Send different data types
ws2.onopen = () => {
ws2.send("text message");
ws2.send(new ArrayBuffer(8));
ws2.send(new Uint8Array([1, 2, 3, 4]));
};
// Handle binary data
ws2.binaryType = "arraybuffer";
ws2.onmessage = (event) => {
if (event.data instanceof ArrayBuffer) {
console.log("Received binary data:", new Uint8Array(event.data));
} else {
console.log("Received text:", event.data);
}
};Additional WebSocket utilities for advanced operations.
/**
* Send a ping frame to a WebSocket
* @param ws - The WebSocket instance
* @param data - Optional ping payload
* @returns Promise that resolves when ping is sent
*/
function ping(ws: WebSocket, data?: Buffer | ArrayBuffer | ArrayBufferView): Promise<void>;
/**
* WebSocket stream interface for streaming operations
*/
class WebSocketStream {
constructor(url: string | URL, options?: WebSocketStreamOptions);
readonly url: string;
readonly readyState: number;
readonly extensions: string;
readonly protocol: string;
readonly readable: ReadableStream;
readonly writable: WritableStream;
close(closeInfo?: WebSocketCloseInfo): Promise<void>;
}
interface WebSocketStreamOptions {
protocols?: string | string[];
signal?: AbortSignal;
}
interface WebSocketCloseInfo {
code?: number;
reason?: string;
}Usage Examples:
import { WebSocket, WebSocketStream, ping } from "undici-types";
// Ping a WebSocket
const ws = new WebSocket("ws://localhost:8080");
ws.onopen = async () => {
await ping(ws, Buffer.from("ping-data"));
console.log("Ping sent");
};
// Use WebSocket streams
const wsStream = new WebSocketStream("ws://localhost:8080/stream");
const writer = wsStream.writable.getWriter();
await writer.write("Hello from stream");
const reader = wsStream.readable.getReader();
const { value, done } = await reader.read();
if (!done) {
console.log("Received:", value);
}
// Close stream
await wsStream.close({ code: 1000, reason: "Done" });Form data construction and parsing for multipart/form-data.
/**
* Represents form data for multipart/form-data encoding
*/
class FormData {
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;
forEach(
callback: (value: FormDataEntryValue, key: string, parent: FormData) => void,
thisArg?: any
): void;
keys(): IterableIterator<string>;
values(): IterableIterator<FormDataEntryValue>;
entries(): IterableIterator<[string, FormDataEntryValue]>;
[Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]>;
}
type FormDataEntryValue = File | string;
interface File extends Blob {
readonly lastModified: number;
readonly name: string;
readonly webkitRelativePath: string;
}
interface Blob {
readonly size: number;
readonly type: string;
arrayBuffer(): Promise<ArrayBuffer>;
slice(start?: number, end?: number, contentType?: string): Blob;
stream(): ReadableStream<Uint8Array>;
text(): Promise<string>;
}Usage Examples:
import { FormData, fetch } from "undici-types";
// Create form data
const formData = new FormData();
formData.append("username", "john_doe");
formData.append("email", "john@example.com");
formData.append("avatar", new Blob(["image data"], { type: "image/jpeg" }), "avatar.jpg");
// Send form data
const response = await fetch("https://api.example.com/upload", {
method: "POST",
body: formData
});
// Iterate form data
for (const [key, value] of formData) {
console.log(`${key}:`, value);
}
// Check and manipulate form data
if (formData.has("username")) {
console.log("Username:", formData.get("username"));
}
formData.set("username", "jane_doe"); // Replace existing value
formData.delete("email"); // Remove fieldServer-Sent Events (SSE) client implementation for receiving real-time updates from servers.
/**
* EventSource client for Server-Sent Events (SSE)
* Maintains a persistent connection to receive server events
*/
class EventSource extends EventTarget {
constructor(url: string | URL, init?: EventSourceInit);
static readonly CLOSED: 2;
static readonly CONNECTING: 0;
static readonly OPEN: 1;
readonly CLOSED: 2;
readonly CONNECTING: 0;
readonly OPEN: 1;
/** Current connection state */
readonly readyState: 0 | 1 | 2;
/** Event source URL */
readonly url: string;
/** Whether credentials are included in requests */
readonly withCredentials: boolean;
/** Event handler for connection errors */
onerror: ((this: EventSource, ev: ErrorEvent) => any) | null;
/** Event handler for received messages */
onmessage: ((this: EventSource, ev: MessageEvent) => any) | null;
/** Event handler for connection open */
onopen: ((this: EventSource, ev: Event) => any) | null;
/** Close the connection */
close(): void;
addEventListener<K extends keyof EventSourceEventMap>(
type: K,
listener: (this: EventSource, ev: EventSourceEventMap[K]) => any,
options?: boolean | AddEventListenerOptions
): void;
removeEventListener<K extends keyof EventSourceEventMap>(
type: K,
listener: (this: EventSource, ev: EventSourceEventMap[K]) => any,
options?: boolean | EventListenerOptions
): void;
}
interface EventSourceInit {
/** Include credentials with requests */
withCredentials?: boolean;
/** Custom dispatcher for the connection */
dispatcher?: Dispatcher;
/** Node.js specific options */
node?: {
/** Custom dispatcher for Node.js */
dispatcher?: Dispatcher;
/** Reconnection delay in milliseconds */
reconnectionTime?: number;
};
}
interface EventSourceEventMap {
/** Connection error event */
error: ErrorEvent;
/** Message received event */
message: MessageEvent;
/** Connection opened event */
open: Event;
}Usage Examples:
import { EventSource } from "undici-types";
// Basic EventSource connection
const eventSource = new EventSource("https://api.example.com/events");
// Handle connection events
eventSource.onopen = (event) => {
console.log("EventSource connection opened");
console.log("Ready state:", eventSource.readyState); // 1 (OPEN)
};
eventSource.onmessage = (event) => {
console.log("Received message:", event.data);
// Parse JSON data if expected
try {
const data = JSON.parse(event.data);
console.log("Parsed data:", data);
} catch (e) {
console.log("Plain text message:", event.data);
}
};
eventSource.onerror = (event) => {
console.error("EventSource error:", event);
if (eventSource.readyState === EventSource.CLOSED) {
console.log("Connection closed");
} else {
console.log("Connection error, will retry...");
}
};
// EventSource with credentials and custom options
const authEventSource = new EventSource("https://api.example.com/private-events", {
withCredentials: true,
node: {
reconnectionTime: 5000 // 5 second reconnection delay
}
});
// Listen for custom event types
authEventSource.addEventListener("user-update", (event) => {
console.log("User update received:", event.data);
});
authEventSource.addEventListener("notification", (event) => {
const notification = JSON.parse(event.data);
showNotification(notification.title, notification.message);
});
// Graceful shutdown
window.addEventListener("beforeunload", () => {
eventSource.close();
authEventSource.close();
});
// Manual connection management
function connectWithRetry(url: string, maxRetries = 3) {
let retryCount = 0;
function connect() {
const es = new EventSource(url);
es.onerror = (event) => {
if (es.readyState === EventSource.CLOSED && retryCount < maxRetries) {
console.log(`Retrying connection... (${retryCount + 1}/${maxRetries})`);
retryCount++;
setTimeout(connect, 1000 * Math.pow(2, retryCount)); // Exponential backoff
} else if (retryCount >= maxRetries) {
console.error("Max retries reached, giving up");
}
};
es.onopen = () => {
retryCount = 0; // Reset retry count on successful connection
console.log("Connected successfully");
};
return es;
}
return connect();
}
const resilientEventSource = connectWithRetry("https://api.example.com/stream");Common interface providing body parsing methods for Request and Response classes.
/**
* Common body parsing methods for Request and Response
*/
interface BodyMixin {
/** Raw body stream */
readonly body: ReadableStream<Uint8Array> | null;
/** Whether the body has been consumed */
readonly bodyUsed: boolean;
/** Parse body as ArrayBuffer */
arrayBuffer(): Promise<ArrayBuffer>;
/** Parse body as Blob */
blob(): Promise<Blob>;
/** Parse body as Uint8Array */
bytes(): Promise<Uint8Array>;
/** Parse body as FormData (deprecated for server parsing) */
formData(): Promise<FormData>;
/** Parse body as JSON */
json(): Promise<unknown>;
/** Parse body as text string */
text(): Promise<string>;
}
type BodyInit =
| ArrayBuffer
| AsyncIterable<Uint8Array>
| Blob
| FormData
| Iterable<Uint8Array>
| NodeJS.ArrayBufferView
| URLSearchParams
| null
| string;Install with Tessl CLI
npx tessl i tessl/npm-undici-types