Universal HTTP fetch library with intelligent parsing, error handling, retry logic, and cross-environment compatibility
npx @tessl/cli install tessl/npm-ofetch@1.4.0ofetch is a universal HTTP fetch library that works seamlessly across Node.js, browsers, and web workers. It enhances the standard fetch API with intelligent response parsing, comprehensive error handling with retry logic, request/response interceptor support, and TypeScript-first design with cross-environment compatibility.
npm install ofetchimport { ofetch, $fetch } from "ofetch";For CommonJS:
const { ofetch, $fetch } = require("ofetch");For Node.js optimized version (conditional exports):
// Uses Node.js-specific optimizations automatically
import { ofetch } from "ofetch";
// Explicit Node.js imports (same API, Node.js optimizations)
import { ofetch, createNodeFetch } from "ofetch/node";import { ofetch } from "ofetch";
// Simple GET request with automatic JSON parsing
const data = await ofetch("https://api.example.com/users");
// POST request with JSON body
const newUser = await ofetch("https://api.example.com/users", {
method: "POST",
body: { name: "John", email: "john@example.com" }
});
// Configure base URL and default options
const api = ofetch.create({
baseURL: "https://api.example.com",
headers: { "Authorization": "Bearer token" }
});
const users = await api("/users");ofetch is built around several key components:
Primary fetch functionality with enhanced features like automatic JSON parsing, intelligent error handling, and cross-environment compatibility.
interface $Fetch {
<T = any, R extends ResponseType = "json">(
request: FetchRequest,
options?: FetchOptions<R>
): Promise<MappedResponseType<R, T>>;
raw<T = any, R extends ResponseType = "json">(
request: FetchRequest,
options?: FetchOptions<R>
): Promise<FetchResponse<MappedResponseType<R, T>>>;
native: Fetch;
create(defaults: FetchOptions, globalOptions?: CreateFetchOptions): $Fetch;
}Create customized fetch instances with default options, base URLs, and platform-specific settings.
function createFetch(globalOptions?: CreateFetchOptions): $Fetch;
interface CreateFetchOptions {
defaults?: FetchOptions;
fetch?: Fetch;
Headers?: typeof Headers;
AbortController?: typeof AbortController;
}Lifecycle hooks for modifying requests, handling responses, and implementing custom logic during the fetch process.
interface FetchHooks<T = any, R extends ResponseType = ResponseType> {
onRequest?: MaybeArray<FetchHook<FetchContext<T, R>>>;
onRequestError?: MaybeArray<FetchHook<FetchContext<T, R> & { error: Error }>>;
onResponse?: MaybeArray<FetchHook<FetchContext<T, R> & { response: FetchResponse<T> }>>;
onResponseError?: MaybeArray<FetchHook<FetchContext<T, R> & { response: FetchResponse<T> }>>;
}
type MaybeArray<T> = T | T[];
type MaybePromise<T> = T | Promise<T>;
type FetchHook<C extends FetchContext = FetchContext> = (
context: C
) => MaybePromise<void>;Enhanced error objects with request/response context, automatic retry for specific status codes, and configurable retry strategies.
class FetchError<T = any> extends Error implements IFetchError<T> {
request?: FetchRequest;
options?: FetchOptions;
response?: FetchResponse<T>;
data?: T;
status?: number;
statusText?: string;
statusCode?: number;
statusMessage?: string;
}Low-level utility functions for request processing, response type detection, and option merging used internally and available for custom implementations.
function isPayloadMethod(method?: string): boolean;
function isJSONSerializable(value: any): boolean;
function detectResponseType(contentType?: string): ResponseType;
function resolveFetchOptions<R extends ResponseType, T>(
request: FetchRequest,
input: FetchOptions<R, T> | undefined,
defaults: FetchOptions<R, T> | undefined,
Headers: typeof globalThis.Headers
): ResolvedFetchOptions<R, T>;
function callHooks<C extends FetchContext>(
context: C,
hooks: FetchHook<C> | FetchHook<C>[] | undefined
): Promise<void>;
function createNodeFetch(): typeof globalThis.fetch;type ResponseType = "json" | "text" | "blob" | "arrayBuffer" | "stream";
type FetchRequest = RequestInfo;
interface FetchOptions<R extends ResponseType = ResponseType, T = any>
extends Omit<RequestInit, "body">, FetchHooks<T, R> {
baseURL?: string;
body?: RequestInit["body"] | Record<string, any>;
ignoreResponseError?: boolean;
params?: Record<string, any>;
query?: Record<string, any>;
parseResponse?: (responseText: string) => any;
responseType?: R;
duplex?: "half" | undefined;
dispatcher?: InstanceType<typeof import("undici").Dispatcher>;
agent?: unknown;
timeout?: number;
retry?: number | false;
retryDelay?: number | ((context: FetchContext<T, R>) => number);
retryStatusCodes?: number[];
}
interface ResolvedFetchOptions<R extends ResponseType = ResponseType, T = any>
extends FetchOptions<R, T> {
headers: Headers;
}
interface FetchResponse<T> extends Response {
_data?: T;
}
interface FetchContext<T = any, R extends ResponseType = ResponseType> {
request: FetchRequest;
options: ResolvedFetchOptions<R>;
response?: FetchResponse<T>;
error?: Error;
}
type MappedResponseType<R extends ResponseType, JsonType = any> =
R extends keyof ResponseMap ? ResponseMap[R] : JsonType;
interface ResponseMap {
blob: Blob;
text: string;
arrayBuffer: ArrayBuffer;
stream: ReadableStream<Uint8Array>;
}
interface SearchParameters {
[key: string]: any;
}
type Fetch = typeof globalThis.fetch;
type GlobalOptions = Pick<FetchOptions, "timeout" | "retry" | "retryDelay">;
interface IFetchError<T = any> extends Error {
request?: FetchRequest;
options?: FetchOptions;
response?: FetchResponse<T>;
data?: T;
status?: number;
statusText?: string;
statusCode?: number;
statusMessage?: string;
}