0
# Web Standards APIs
1
2
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.
3
4
## Capabilities
5
6
### Fetch API
7
8
Standards-compliant fetch implementation for HTTP requests.
9
10
```typescript { .api }
11
/**
12
* Perform HTTP requests using the standard web fetch API
13
* @param input - URL string or Request object
14
* @param init - Request configuration options
15
* @returns Promise resolving to Response object
16
*/
17
function fetch(
18
input: RequestInfo,
19
init?: RequestInit
20
): Promise<Response>;
21
22
type RequestInfo = Request | string | URL;
23
24
interface RequestInit {
25
method?: string;
26
headers?: HeadersInit;
27
body?: BodyInit;
28
mode?: RequestMode;
29
credentials?: RequestCredentials;
30
cache?: RequestCache;
31
redirect?: RequestRedirect;
32
referrer?: string;
33
referrerPolicy?: ReferrerPolicy;
34
integrity?: string;
35
keepalive?: boolean;
36
signal?: AbortSignal;
37
window?: null;
38
duplex?: RequestDuplex;
39
}
40
41
type RequestMode = "cors" | "no-cors" | "same-origin" | "navigate";
42
type RequestCredentials = "omit" | "same-origin" | "include";
43
type RequestCache = "default" | "no-store" | "reload" | "no-cache" | "force-cache" | "only-if-cached";
44
type RequestRedirect = "follow" | "error" | "manual";
45
type RequestDuplex = "half";
46
```
47
48
**Usage Examples:**
49
50
```typescript
51
import { fetch } from "undici-types";
52
53
// Simple GET request
54
const response = await fetch("https://api.example.com/users");
55
const users = await response.json();
56
57
// POST request with JSON
58
const response = await fetch("https://api.example.com/users", {
59
method: "POST",
60
headers: {
61
"Content-Type": "application/json"
62
},
63
body: JSON.stringify({
64
name: "John Doe",
65
email: "john@example.com"
66
})
67
});
68
69
// Request with AbortController
70
const controller = new AbortController();
71
setTimeout(() => controller.abort(), 5000);
72
73
try {
74
const response = await fetch("https://api.example.com/slow", {
75
signal: controller.signal
76
});
77
} catch (error) {
78
if (error.name === 'AbortError') {
79
console.log('Request was aborted');
80
}
81
}
82
```
83
84
### Request Class
85
86
Represents an HTTP request with full configuration options.
87
88
```typescript { .api }
89
/**
90
* Represents an HTTP request
91
*/
92
class Request implements BodyMixin {
93
constructor(input: RequestInfo, init?: RequestInit);
94
95
readonly method: string;
96
readonly url: string;
97
readonly headers: Headers;
98
readonly body: ReadableStream<Uint8Array> | null;
99
readonly bodyUsed: boolean;
100
readonly cache: RequestCache;
101
readonly credentials: RequestCredentials;
102
readonly destination: RequestDestination;
103
readonly integrity: string;
104
readonly keepalive: boolean;
105
readonly mode: RequestMode;
106
readonly redirect: RequestRedirect;
107
readonly referrer: string;
108
readonly referrerPolicy: ReferrerPolicy;
109
readonly signal: AbortSignal;
110
111
clone(): Request;
112
113
// BodyMixin methods
114
arrayBuffer(): Promise<ArrayBuffer>;
115
blob(): Promise<Blob>;
116
formData(): Promise<FormData>;
117
json(): Promise<any>;
118
text(): Promise<string>;
119
}
120
121
type RequestDestination = "" | "audio" | "audioworklet" | "document" | "embed" | "font" | "frame" | "iframe" | "image" | "manifest" | "object" | "paintworklet" | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track" | "video" | "worker" | "xslt";
122
```
123
124
**Usage Examples:**
125
126
```typescript
127
import { Request, fetch } from "undici-types";
128
129
// Create reusable request
130
const apiRequest = new Request("https://api.example.com/data", {
131
method: "POST",
132
headers: {
133
"Content-Type": "application/json",
134
"Authorization": "Bearer token123"
135
},
136
body: JSON.stringify({ query: "users" })
137
});
138
139
// Use request multiple times
140
const response1 = await fetch(apiRequest.clone());
141
const response2 = await fetch(apiRequest.clone());
142
143
// Access request properties
144
console.log(apiRequest.method); // "POST"
145
console.log(apiRequest.url); // "https://api.example.com/data"
146
console.log(apiRequest.headers.get("Content-Type")); // "application/json"
147
```
148
149
### Response Class
150
151
Represents an HTTP response with body parsing methods.
152
153
```typescript { .api }
154
/**
155
* Represents an HTTP response
156
*/
157
class Response implements BodyMixin {
158
constructor(body?: BodyInit | null, init?: ResponseInit);
159
160
readonly type: ResponseType;
161
readonly url: string;
162
readonly redirected: boolean;
163
readonly status: number;
164
readonly ok: boolean;
165
readonly statusText: string;
166
readonly headers: Headers;
167
readonly body: ReadableStream<Uint8Array> | null;
168
readonly bodyUsed: boolean;
169
170
clone(): Response;
171
172
// BodyMixin methods
173
arrayBuffer(): Promise<ArrayBuffer>;
174
blob(): Promise<Blob>;
175
formData(): Promise<FormData>;
176
json(): Promise<any>;
177
text(): Promise<string>;
178
179
// Static methods
180
static error(): Response;
181
static json(data: any, init?: ResponseInit): Response;
182
static redirect(url: string | URL, status?: ResponseRedirectStatus): Response;
183
}
184
185
interface ResponseInit {
186
status?: number;
187
statusText?: string;
188
headers?: HeadersInit;
189
}
190
191
type ResponseType = "basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect";
192
type ResponseRedirectStatus = 300 | 301 | 302 | 303 | 307 | 308;
193
```
194
195
**Usage Examples:**
196
197
```typescript
198
import { fetch, Response } from "undici-types";
199
200
// Handle response
201
const response = await fetch("https://api.example.com/users");
202
203
if (response.ok) {
204
const contentType = response.headers.get("content-type");
205
206
if (contentType?.includes("application/json")) {
207
const data = await response.json();
208
console.log(data);
209
} else {
210
const text = await response.text();
211
console.log(text);
212
}
213
} else {
214
console.error(`Error: ${response.status} ${response.statusText}`);
215
}
216
217
// Create custom responses
218
const jsonResponse = Response.json({ message: "Hello" }, {
219
status: 200,
220
headers: { "Custom-Header": "value" }
221
});
222
223
const redirectResponse = Response.redirect("https://example.com/new-location", 301);
224
```
225
226
### Headers Class
227
228
HTTP headers manipulation following web standards.
229
230
```typescript { .api }
231
/**
232
* HTTP headers collection with case-insensitive access
233
*/
234
class Headers {
235
constructor(init?: HeadersInit);
236
237
append(name: string, value: string): void;
238
delete(name: string): void;
239
get(name: string): string | null;
240
getSetCookie(): string[];
241
has(name: string): boolean;
242
set(name: string, value: string): void;
243
244
forEach(callback: (value: string, key: string, parent: Headers) => void, thisArg?: any): void;
245
keys(): IterableIterator<string>;
246
values(): IterableIterator<string>;
247
entries(): IterableIterator<[string, string]>;
248
[Symbol.iterator](): IterableIterator<[string, string]>;
249
}
250
251
type HeadersInit = Headers | Record<string, string | string[]> | Iterable<readonly [string, string]>;
252
```
253
254
**Usage Examples:**
255
256
```typescript
257
import { Headers, fetch } from "undici-types";
258
259
// Create headers object
260
const headers = new Headers();
261
headers.set("Content-Type", "application/json");
262
headers.set("Authorization", "Bearer token123");
263
headers.append("Accept", "application/json");
264
265
// Use with fetch
266
const response = await fetch("https://api.example.com/data", {
267
method: "POST",
268
headers: headers,
269
body: JSON.stringify({ data: "example" })
270
});
271
272
// Iterate headers
273
for (const [name, value] of headers) {
274
console.log(`${name}: ${value}`);
275
}
276
277
// Create from object
278
const headers2 = new Headers({
279
"Content-Type": "text/plain",
280
"Custom-Header": "custom-value"
281
});
282
283
// Create from array
284
const headers3 = new Headers([
285
["Content-Type", "application/xml"],
286
["Accept", "application/xml"]
287
]);
288
```
289
290
### WebSocket
291
292
WebSocket client implementation following web standards.
293
294
```typescript { .api }
295
/**
296
* WebSocket client implementation
297
*/
298
class WebSocket extends EventTarget {
299
constructor(url: string | URL, protocols?: string | string[], options?: WebSocketInit);
300
301
static readonly CONNECTING: 0;
302
static readonly OPEN: 1;
303
static readonly CLOSING: 2;
304
static readonly CLOSED: 3;
305
306
readonly CONNECTING: 0;
307
readonly OPEN: 1;
308
readonly CLOSING: 2;
309
readonly CLOSED: 3;
310
311
readonly url: string;
312
readonly readyState: number;
313
readonly extensions: string;
314
readonly protocol: string;
315
316
binaryType: BinaryType;
317
318
onopen: ((this: WebSocket, ev: Event) => any) | null;
319
onmessage: ((this: WebSocket, ev: MessageEvent) => any) | null;
320
onerror: ((this: WebSocket, ev: ErrorEvent) => any) | null;
321
onclose: ((this: WebSocket, ev: CloseEvent) => any) | null;
322
323
send(data: string | ArrayBuffer | ArrayBufferView | Blob): void;
324
close(code?: number, reason?: string): void;
325
326
addEventListener<K extends keyof WebSocketEventMap>(
327
type: K,
328
listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
329
options?: boolean | AddEventListenerOptions
330
): void;
331
332
removeEventListener<K extends keyof WebSocketEventMap>(
333
type: K,
334
listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
335
options?: boolean | EventListenerOptions
336
): void;
337
}
338
339
interface WebSocketInit {
340
protocols?: string | string[];
341
dispatcher?: Dispatcher;
342
headers?: HeadersInit;
343
followRedirects?: boolean;
344
}
345
346
type BinaryType = "blob" | "arraybuffer";
347
348
interface WebSocketEventMap {
349
"close": CloseEvent;
350
"error": ErrorEvent;
351
"message": MessageEvent;
352
"open": Event;
353
}
354
355
interface CloseEvent extends Event {
356
readonly code: number;
357
readonly reason: string;
358
readonly wasClean: boolean;
359
}
360
361
interface MessageEvent extends Event {
362
readonly data: any;
363
readonly lastEventId: string;
364
readonly origin: string;
365
readonly ports: readonly MessagePort[];
366
readonly source: MessageEventSource | null;
367
}
368
369
interface ErrorEvent extends Event {
370
readonly message: string;
371
readonly filename: string;
372
readonly lineno: number;
373
readonly colno: number;
374
readonly error: any;
375
}
376
```
377
378
**Usage Examples:**
379
380
```typescript
381
import { WebSocket } from "undici-types";
382
383
// Basic WebSocket connection
384
const ws = new WebSocket("ws://localhost:8080");
385
386
ws.onopen = (event) => {
387
console.log("WebSocket connected");
388
ws.send("Hello Server!");
389
};
390
391
ws.onmessage = (event) => {
392
console.log("Received:", event.data);
393
};
394
395
ws.onclose = (event) => {
396
console.log(`WebSocket closed: ${event.code} ${event.reason}`);
397
};
398
399
ws.onerror = (event) => {
400
console.error("WebSocket error:", event.error);
401
};
402
403
// WebSocket with protocols and headers
404
const ws2 = new WebSocket("wss://api.example.com/ws", ["protocol1", "protocol2"], {
405
headers: {
406
"Authorization": "Bearer token123"
407
}
408
});
409
410
// Send different data types
411
ws2.onopen = () => {
412
ws2.send("text message");
413
ws2.send(new ArrayBuffer(8));
414
ws2.send(new Uint8Array([1, 2, 3, 4]));
415
};
416
417
// Handle binary data
418
ws2.binaryType = "arraybuffer";
419
ws2.onmessage = (event) => {
420
if (event.data instanceof ArrayBuffer) {
421
console.log("Received binary data:", new Uint8Array(event.data));
422
} else {
423
console.log("Received text:", event.data);
424
}
425
};
426
```
427
428
### WebSocket Utilities
429
430
Additional WebSocket utilities for advanced operations.
431
432
```typescript { .api }
433
/**
434
* Send a ping frame to a WebSocket
435
* @param ws - The WebSocket instance
436
* @param data - Optional ping payload
437
* @returns Promise that resolves when ping is sent
438
*/
439
function ping(ws: WebSocket, data?: Buffer | ArrayBuffer | ArrayBufferView): Promise<void>;
440
441
/**
442
* WebSocket stream interface for streaming operations
443
*/
444
class WebSocketStream {
445
constructor(url: string | URL, options?: WebSocketStreamOptions);
446
447
readonly url: string;
448
readonly readyState: number;
449
readonly extensions: string;
450
readonly protocol: string;
451
452
readonly readable: ReadableStream;
453
readonly writable: WritableStream;
454
455
close(closeInfo?: WebSocketCloseInfo): Promise<void>;
456
}
457
458
interface WebSocketStreamOptions {
459
protocols?: string | string[];
460
signal?: AbortSignal;
461
}
462
463
interface WebSocketCloseInfo {
464
code?: number;
465
reason?: string;
466
}
467
```
468
469
**Usage Examples:**
470
471
```typescript
472
import { WebSocket, WebSocketStream, ping } from "undici-types";
473
474
// Ping a WebSocket
475
const ws = new WebSocket("ws://localhost:8080");
476
ws.onopen = async () => {
477
await ping(ws, Buffer.from("ping-data"));
478
console.log("Ping sent");
479
};
480
481
// Use WebSocket streams
482
const wsStream = new WebSocketStream("ws://localhost:8080/stream");
483
484
const writer = wsStream.writable.getWriter();
485
await writer.write("Hello from stream");
486
487
const reader = wsStream.readable.getReader();
488
const { value, done } = await reader.read();
489
if (!done) {
490
console.log("Received:", value);
491
}
492
493
// Close stream
494
await wsStream.close({ code: 1000, reason: "Done" });
495
```
496
497
### FormData
498
499
Form data construction and parsing for multipart/form-data.
500
501
```typescript { .api }
502
/**
503
* Represents form data for multipart/form-data encoding
504
*/
505
class FormData {
506
append(name: string, value: string | Blob, fileName?: string): void;
507
delete(name: string): void;
508
get(name: string): FormDataEntryValue | null;
509
getAll(name: string): FormDataEntryValue[];
510
has(name: string): boolean;
511
set(name: string, value: string | Blob, fileName?: string): void;
512
513
forEach(
514
callback: (value: FormDataEntryValue, key: string, parent: FormData) => void,
515
thisArg?: any
516
): void;
517
518
keys(): IterableIterator<string>;
519
values(): IterableIterator<FormDataEntryValue>;
520
entries(): IterableIterator<[string, FormDataEntryValue]>;
521
[Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]>;
522
}
523
524
type FormDataEntryValue = File | string;
525
526
interface File extends Blob {
527
readonly lastModified: number;
528
readonly name: string;
529
readonly webkitRelativePath: string;
530
}
531
532
interface Blob {
533
readonly size: number;
534
readonly type: string;
535
536
arrayBuffer(): Promise<ArrayBuffer>;
537
slice(start?: number, end?: number, contentType?: string): Blob;
538
stream(): ReadableStream<Uint8Array>;
539
text(): Promise<string>;
540
}
541
```
542
543
**Usage Examples:**
544
545
```typescript
546
import { FormData, fetch } from "undici-types";
547
548
// Create form data
549
const formData = new FormData();
550
formData.append("username", "john_doe");
551
formData.append("email", "john@example.com");
552
formData.append("avatar", new Blob(["image data"], { type: "image/jpeg" }), "avatar.jpg");
553
554
// Send form data
555
const response = await fetch("https://api.example.com/upload", {
556
method: "POST",
557
body: formData
558
});
559
560
// Iterate form data
561
for (const [key, value] of formData) {
562
console.log(`${key}:`, value);
563
}
564
565
// Check and manipulate form data
566
if (formData.has("username")) {
567
console.log("Username:", formData.get("username"));
568
}
569
570
formData.set("username", "jane_doe"); // Replace existing value
571
formData.delete("email"); // Remove field
572
```
573
574
### EventSource
575
576
Server-Sent Events (SSE) client implementation for receiving real-time updates from servers.
577
578
```typescript { .api }
579
/**
580
* EventSource client for Server-Sent Events (SSE)
581
* Maintains a persistent connection to receive server events
582
*/
583
class EventSource extends EventTarget {
584
constructor(url: string | URL, init?: EventSourceInit);
585
586
static readonly CLOSED: 2;
587
static readonly CONNECTING: 0;
588
static readonly OPEN: 1;
589
590
readonly CLOSED: 2;
591
readonly CONNECTING: 0;
592
readonly OPEN: 1;
593
594
/** Current connection state */
595
readonly readyState: 0 | 1 | 2;
596
597
/** Event source URL */
598
readonly url: string;
599
600
/** Whether credentials are included in requests */
601
readonly withCredentials: boolean;
602
603
/** Event handler for connection errors */
604
onerror: ((this: EventSource, ev: ErrorEvent) => any) | null;
605
606
/** Event handler for received messages */
607
onmessage: ((this: EventSource, ev: MessageEvent) => any) | null;
608
609
/** Event handler for connection open */
610
onopen: ((this: EventSource, ev: Event) => any) | null;
611
612
/** Close the connection */
613
close(): void;
614
615
addEventListener<K extends keyof EventSourceEventMap>(
616
type: K,
617
listener: (this: EventSource, ev: EventSourceEventMap[K]) => any,
618
options?: boolean | AddEventListenerOptions
619
): void;
620
621
removeEventListener<K extends keyof EventSourceEventMap>(
622
type: K,
623
listener: (this: EventSource, ev: EventSourceEventMap[K]) => any,
624
options?: boolean | EventListenerOptions
625
): void;
626
}
627
628
interface EventSourceInit {
629
/** Include credentials with requests */
630
withCredentials?: boolean;
631
632
/** Custom dispatcher for the connection */
633
dispatcher?: Dispatcher;
634
635
/** Node.js specific options */
636
node?: {
637
/** Custom dispatcher for Node.js */
638
dispatcher?: Dispatcher;
639
640
/** Reconnection delay in milliseconds */
641
reconnectionTime?: number;
642
};
643
}
644
645
interface EventSourceEventMap {
646
/** Connection error event */
647
error: ErrorEvent;
648
649
/** Message received event */
650
message: MessageEvent;
651
652
/** Connection opened event */
653
open: Event;
654
}
655
```
656
657
**Usage Examples:**
658
659
```typescript
660
import { EventSource } from "undici-types";
661
662
// Basic EventSource connection
663
const eventSource = new EventSource("https://api.example.com/events");
664
665
// Handle connection events
666
eventSource.onopen = (event) => {
667
console.log("EventSource connection opened");
668
console.log("Ready state:", eventSource.readyState); // 1 (OPEN)
669
};
670
671
eventSource.onmessage = (event) => {
672
console.log("Received message:", event.data);
673
674
// Parse JSON data if expected
675
try {
676
const data = JSON.parse(event.data);
677
console.log("Parsed data:", data);
678
} catch (e) {
679
console.log("Plain text message:", event.data);
680
}
681
};
682
683
eventSource.onerror = (event) => {
684
console.error("EventSource error:", event);
685
686
if (eventSource.readyState === EventSource.CLOSED) {
687
console.log("Connection closed");
688
} else {
689
console.log("Connection error, will retry...");
690
}
691
};
692
693
// EventSource with credentials and custom options
694
const authEventSource = new EventSource("https://api.example.com/private-events", {
695
withCredentials: true,
696
node: {
697
reconnectionTime: 5000 // 5 second reconnection delay
698
}
699
});
700
701
// Listen for custom event types
702
authEventSource.addEventListener("user-update", (event) => {
703
console.log("User update received:", event.data);
704
});
705
706
authEventSource.addEventListener("notification", (event) => {
707
const notification = JSON.parse(event.data);
708
showNotification(notification.title, notification.message);
709
});
710
711
// Graceful shutdown
712
window.addEventListener("beforeunload", () => {
713
eventSource.close();
714
authEventSource.close();
715
});
716
717
// Manual connection management
718
function connectWithRetry(url: string, maxRetries = 3) {
719
let retryCount = 0;
720
721
function connect() {
722
const es = new EventSource(url);
723
724
es.onerror = (event) => {
725
if (es.readyState === EventSource.CLOSED && retryCount < maxRetries) {
726
console.log(`Retrying connection... (${retryCount + 1}/${maxRetries})`);
727
retryCount++;
728
setTimeout(connect, 1000 * Math.pow(2, retryCount)); // Exponential backoff
729
} else if (retryCount >= maxRetries) {
730
console.error("Max retries reached, giving up");
731
}
732
};
733
734
es.onopen = () => {
735
retryCount = 0; // Reset retry count on successful connection
736
console.log("Connected successfully");
737
};
738
739
return es;
740
}
741
742
return connect();
743
}
744
745
const resilientEventSource = connectWithRetry("https://api.example.com/stream");
746
```
747
748
### BodyMixin Interface
749
750
Common interface providing body parsing methods for Request and Response classes.
751
752
```typescript { .api }
753
/**
754
* Common body parsing methods for Request and Response
755
*/
756
interface BodyMixin {
757
/** Raw body stream */
758
readonly body: ReadableStream<Uint8Array> | null;
759
760
/** Whether the body has been consumed */
761
readonly bodyUsed: boolean;
762
763
/** Parse body as ArrayBuffer */
764
arrayBuffer(): Promise<ArrayBuffer>;
765
766
/** Parse body as Blob */
767
blob(): Promise<Blob>;
768
769
/** Parse body as Uint8Array */
770
bytes(): Promise<Uint8Array>;
771
772
/** Parse body as FormData (deprecated for server parsing) */
773
formData(): Promise<FormData>;
774
775
/** Parse body as JSON */
776
json(): Promise<unknown>;
777
778
/** Parse body as text string */
779
text(): Promise<string>;
780
}
781
782
type BodyInit =
783
| ArrayBuffer
784
| AsyncIterable<Uint8Array>
785
| Blob
786
| FormData
787
| Iterable<Uint8Array>
788
| NodeJS.ArrayBufferView
789
| URLSearchParams
790
| null
791
| string;
792
```