A window.fetch polyfill for browsers that don't natively support the Fetch API
npx @tessl/cli install tessl/npm-whatwg-fetch@3.6.0whatwg-fetch is a comprehensive polyfill implementation of the Fetch API for web browsers that don't natively support window.fetch. It provides a Promise-based mechanism for making HTTP requests as a modern replacement for XMLHttpRequest, following the WHATWG Fetch specification standards.
npm install whatwg-fetchimport 'whatwg-fetch'
// Global polyfill - automatically installs window.fetch and related APIs
// Only installs when native fetch is not available
window.fetch(...)For explicit access to polyfill components:
import { fetch as fetchPolyfill, Headers, Request, Response, DOMException } from 'whatwg-fetch'
// Use polyfill explicitly alongside or instead of native
fetchPolyfill(...) // Always uses polyfill implementation
window.fetch(...) // Uses native browser version when availableCommonJS:
require('whatwg-fetch');
// Global polyfill - automatically installs global.fetch, Headers, Request, Response
// Only installs when native fetch is not available
fetch(...)import 'whatwg-fetch'
// Simple GET request
fetch('/api/users')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Request failed:', error));
// POST with JSON data
fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'John Doe',
email: 'john@example.com'
})
})
.then(response => response.json())
.then(data => console.log('User created:', data));
// File upload with FormData
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('description', 'Profile photo');
fetch('/upload', {
method: 'POST',
body: formData
})
.then(response => response.json());whatwg-fetch implements the complete Fetch API specification with these core components:
Core fetch functionality for making HTTP requests with full Promise support.
/**
* Make an HTTP request
* @param input - URL string, URL object, or Request object
* @param init - Optional request configuration
* @returns Promise that resolves to Response object
*/
function fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
// Polyfill identification property
fetch.polyfill: boolean;
type RequestInfo = Request | string;
interface RequestInit {
method?: string;
headers?: HeadersInit;
body?: BodyInit;
credentials?: 'omit' | 'same-origin' | 'include';
mode?: string;
referrer?: string;
signal?: AbortSignal;
}
type ModeType = string;
type BodyInit = string | URLSearchParams | FormData | Blob | ArrayBuffer | ArrayBufferView | null;
type HeadersInit = Headers | Record<string, string> | [string, string][];Usage Examples:
// GET request
fetch('/api/data')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
});
// POST request with credentials
fetch('/api/login', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username, password })
});
// Request with abort signal
const controller = new AbortController();
fetch('/api/data', {
signal: controller.signal
});
// Cancel the request
controller.abort();Create and configure HTTP requests with full type safety and validation.
/**
* Create a new Request object
* @param input - URL string, URL object, or existing Request
* @param options - Request configuration options
*/
class Request {
constructor(input: RequestInfo, options?: RequestInit);
// Properties
readonly url: string;
readonly method: string;
readonly headers: Headers;
readonly credentials: 'omit' | 'same-origin' | 'include';
readonly mode: ModeType;
readonly signal: AbortSignal;
readonly referrer: string;
readonly bodyUsed: boolean;
// Methods
clone(): Request;
arrayBuffer(): Promise<ArrayBuffer>;
blob(): Promise<Blob>;
formData(): Promise<FormData>;
json(): Promise<any>;
text(): Promise<string>;
}Usage Examples:
// Create request with configuration
const request = new Request('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
},
body: JSON.stringify({ name: 'Alice' })
});
// Clone request for retry logic
const originalRequest = new Request('/api/data', { method: 'GET' });
const retryRequest = originalRequest.clone();
// Read request body
const requestData = await request.json();Handle HTTP responses with comprehensive body parsing and metadata access.
/**
* HTTP Response representation
* @param bodyInit - Response body content
* @param options - Response configuration
*/
class Response {
constructor(bodyInit?: BodyInit, options?: ResponseInit);
// Properties
readonly url: string;
readonly status: number;
readonly statusText: string;
readonly ok: boolean;
readonly headers: Headers;
readonly type: 'default' | 'error';
readonly bodyUsed: boolean;
// Methods
clone(): Response;
arrayBuffer(): Promise<ArrayBuffer>;
blob(): Promise<Blob>;
formData(): Promise<FormData>;
json(): Promise<any>;
text(): Promise<string>;
// Static methods
static error(): Response;
static redirect(url: string, status?: number): Response;
}
interface ResponseInit {
status?: number;
statusText?: string;
headers?: HeadersInit;
url?: string;
}Usage Examples:
// Handle response with status checking
fetch('/api/data')
.then(response => {
console.log(`Status: ${response.status} ${response.statusText}`);
console.log(`Content-Type: ${response.headers.get('Content-Type')}`);
if (response.ok) {
return response.json();
} else {
throw new Error(`Request failed: ${response.status}`);
}
});
// Create custom responses
const successResponse = new Response(
JSON.stringify({ message: 'Success' }),
{
status: 200,
statusText: 'OK',
headers: { 'Content-Type': 'application/json' }
}
);
const errorResponse = Response.error();
const redirectResponse = Response.redirect('/login', 302);Manage HTTP headers with case-insensitive operations and iteration support.
/**
* HTTP headers collection
* @param init - Initial headers as object, array, or Headers instance
*/
class Headers {
constructor(init?: HeadersInit);
// Methods
append(name: string, value: string): void;
delete(name: string): void;
get(name: string): string | null;
has(name: string): boolean;
set(name: string, value: string): void;
forEach(callback: (value: string, name: string, headers: Headers) => void, thisArg?: any): void;
// Iterator methods
keys(): IterableIterator<string>;
values(): IterableIterator<string>;
entries(): IterableIterator<[string, string]>;
[Symbol.iterator](): IterableIterator<[string, string]>;
}Usage Examples:
// Create headers from object
const headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
});
// Add headers
headers.append('X-Custom-Header', 'value1');
headers.append('X-Custom-Header', 'value2'); // Combines: "value1, value2"
// Check and get headers
if (headers.has('Content-Type')) {
console.log(headers.get('Content-Type')); // "application/json"
}
// Iterate over headers
headers.forEach((value, name) => {
console.log(`${name}: ${value}`);
});
// Use with fetch
fetch('/api/data', {
headers: new Headers({
'Accept': 'application/json',
'X-API-Key': 'key123'
})
});Handle fetch errors and request abortion with standard exception types.
/**
* DOM Exception for fetch errors
* @param message - Error message
* @param name - Exception name (e.g., 'AbortError')
*/
class DOMException extends Error {
constructor(message?: string, name?: string);
readonly name: string;
readonly message: string;
readonly stack: string;
}Usage Examples:
// Handle different error types
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.then(response => response.json())
.catch(error => {
if (error.name === 'AbortError') {
console.log('Request was aborted');
} else if (error instanceof TypeError) {
console.log('Network error or request failed');
} else {
console.log('Other error:', error.message);
}
});
// Abort after timeout
setTimeout(() => controller.abort(), 5000);
// Check if polyfill is active
if (fetch.polyfill) {
console.log('Using whatwg-fetch polyfill');
} else {
console.log('Using native fetch implementation');
}
// Custom error handling for HTTP errors
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
} else {
const error = new Error(response.statusText);
error.response = response;
throw error;
}
}
fetch('/api/data')
.then(checkStatus)
.then(response => response.json());The polyfill automatically detects native fetch support and only installs itself when needed.
credentials: 'same-origin' for maximum compatibility with cookiesfetch.polyfill property to determine if polyfill is activecache: 'no-store' or cache: 'no-cache' get timestamp parameters added