CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ofetch

Universal HTTP fetch library with intelligent parsing, error handling, retry logic, and cross-environment compatibility

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

utilities.mddocs/

Utility Functions

Low-level utility functions for request processing, response type detection, option merging, and hook execution used internally by ofetch and available for custom implementations.

Capabilities

Request Method Validation

Determines if an HTTP method typically includes a request payload/body.

/**
 * Check if HTTP method typically includes a request body
 * @param method - HTTP method string (default: "GET")
 * @returns True if method typically includes payload (PATCH, POST, PUT, DELETE)
 */
function isPayloadMethod(method?: string): boolean;

Usage Examples:

import { isPayloadMethod } from "ofetch";

console.log(isPayloadMethod("GET"));    // false
console.log(isPayloadMethod("POST"));   // true
console.log(isPayloadMethod("PUT"));    // true
console.log(isPayloadMethod("DELETE")); // true
console.log(isPayloadMethod("PATCH"));  // true

// Use in custom logic
function prepareRequest(method: string, data: any) {
  if (isPayloadMethod(method) && data) {
    return { method, body: JSON.stringify(data) };
  }
  return { method };
}

JSON Serialization Check

Determines if a value can be safely JSON serialized.

/**
 * Check if a value can be JSON serialized
 * @param value - Value to check for JSON serializability
 * @returns True if value can be safely JSON stringified
 */
function isJSONSerializable(value: any): boolean;

Usage Examples:

import { isJSONSerializable } from "ofetch";

// Primitive types
console.log(isJSONSerializable("string"));    // true
console.log(isJSONSerializable(123));         // true
console.log(isJSONSerializable(true));        // true
console.log(isJSONSerializable(null));        // true

// Objects and arrays
console.log(isJSONSerializable({ key: "value" })); // true
console.log(isJSONSerializable([1, 2, 3]));        // true

// Non-serializable types
console.log(isJSONSerializable(undefined));        // false
console.log(isJSONSerializable(() => {}));         // false
console.log(isJSONSerializable(Symbol("test")));   // false
console.log(isJSONSerializable(new ArrayBuffer(8))); // false

// Objects with toJSON method
class CustomClass {
  toJSON() { return { serialized: true }; }
}
console.log(isJSONSerializable(new CustomClass())); // true

// Use in request preparation
function prepareBody(data: any) {
  if (isJSONSerializable(data)) {
    return JSON.stringify(data);
  }
  return data; // Return as-is for FormData, Buffer, etc.
}

Response Type Detection

Automatically detects the appropriate response type based on Content-Type header.

/**
 * Detect response type from Content-Type header
 * @param contentType - Content-Type header value
 * @returns Appropriate ResponseType for parsing
 */
function detectResponseType(contentType?: string): ResponseType;

type ResponseType = "json" | "text" | "blob" | "arrayBuffer" | "stream";

Usage Examples:

import { detectResponseType } from "ofetch";

// JSON content types
console.log(detectResponseType("application/json"));           // "json"
console.log(detectResponseType("application/json; charset=utf-8")); // "json"
console.log(detectResponseType("application/ld+json"));        // "json"

// Text content types  
console.log(detectResponseType("text/plain"));                 // "text"
console.log(detectResponseType("text/html"));                  // "text"
console.log(detectResponseType("application/xml"));            // "text"
console.log(detectResponseType("image/svg+xml"));              // "text"

// Binary content types
console.log(detectResponseType("image/png"));                  // "blob"
console.log(detectResponseType("application/pdf"));            // "blob"
console.log(detectResponseType("video/mp4"));                  // "blob"

// Default behavior
console.log(detectResponseType(""));                           // "json"
console.log(detectResponseType(undefined));                    // "json"

// Use in custom response handling
async function handleResponse(response: Response) {
  const contentType = response.headers.get("content-type") || "";
  const responseType = detectResponseType(contentType);
  
  switch (responseType) {
    case "json":
      return await response.json();
    case "text":
      return await response.text();
    case "blob":
      return await response.blob();
    default:
      return await response.text();
  }
}

Options Resolution

Merges fetch options with defaults, handling headers and query parameters properly.

/**
 * Resolve and merge fetch options with defaults
 * @param request - The fetch request (URL or Request object)
 * @param input - Input fetch options
 * @param defaults - Default fetch options
 * @param Headers - Headers constructor
 * @returns Merged and resolved fetch options
 */
function resolveFetchOptions<R extends ResponseType = ResponseType, T = any>(
  request: FetchRequest,
  input: FetchOptions<R, T> | undefined,
  defaults: FetchOptions<R, T> | undefined,
  Headers: typeof globalThis.Headers
): ResolvedFetchOptions<R, T>;

Usage Examples:

import { resolveFetchOptions } from "ofetch";

// Merge options with defaults
const defaults = {
  headers: { "Content-Type": "application/json" },
  timeout: 5000,
  query: { version: "v1" }
};

const input = {
  headers: { "Authorization": "Bearer token" },
  query: { filter: "active" }
};

const resolved = resolveFetchOptions(
  "https://api.example.com/users",
  input,
  defaults,
  Headers
);

console.log(resolved.headers.get("Content-Type"));  // "application/json"
console.log(resolved.headers.get("Authorization")); // "Bearer token"
console.log(resolved.query); // { version: "v1", filter: "active" }
console.log(resolved.timeout); // 5000

// Use in custom fetch implementation
function customFetch(url: string, options?: any) {
  const resolved = resolveFetchOptions(url, options, myDefaults, Headers);
  return fetch(url, resolved);
}

Hook Execution

Executes interceptor hooks in sequence, supporting both single hooks and arrays.

/**
 * Execute fetch hooks in sequence
 * @param context - Fetch context containing request, options, response, error
 * @param hooks - Single hook function or array of hook functions
 * @returns Promise that resolves when all hooks complete
 */
async function callHooks<C extends FetchContext = FetchContext>(
  context: C,
  hooks: FetchHook<C> | FetchHook<C>[] | undefined
): Promise<void>;

type FetchHook<C extends FetchContext = FetchContext> = (
  context: C
) => MaybePromise<void>;

type MaybePromise<T> = T | Promise<T>;

Usage Examples:

import { callHooks } from "ofetch";

// Single hook
const singleHook = ({ options }: FetchContext) => {
  options.headers.set("X-Custom", "value");
};

// Multiple hooks
const hooks = [
  ({ options }: FetchContext) => {
    console.log("Hook 1");
    options.headers.set("X-Hook1", "executed");
  },
  async ({ options }: FetchContext) => {
    console.log("Hook 2");
    const token = await getAuthToken();
    options.headers.set("Authorization", `Bearer ${token}`);
  }
];

// Execute hooks
const context = {
  request: "https://api.example.com/data",
  options: { headers: new Headers() },
  response: undefined,
  error: undefined
};

await callHooks(context, singleHook);
await callHooks(context, hooks);

// Use in custom interceptor system
class CustomFetcher {
  private onRequestHooks: FetchHook[] = [];
  
  addHook(hook: FetchHook) {
    this.onRequestHooks.push(hook);
  }
  
  async fetch(url: string, options: any = {}) {
    const context = { request: url, options: { ...options, headers: new Headers(options.headers) } };
    
    // Execute all hooks
    await callHooks(context, this.onRequestHooks);
    
    return fetch(context.request, context.options);
  }
}

Node.js Fetch Creation

Creates a Node.js-optimized fetch function with optional keep-alive support.

/**
 * Create Node.js-specific fetch with keep-alive support
 * Uses FETCH_KEEP_ALIVE environment variable to enable persistent connections
 * @returns Fetch function with Node.js optimizations
 */
function createNodeFetch(): typeof globalThis.fetch;

Usage Examples:

import { createNodeFetch } from "ofetch/node";

// Enable keep-alive via environment variable
process.env.FETCH_KEEP_ALIVE = "true";
const fetch = createNodeFetch();

// Use the Node.js optimized fetch
const response = await fetch("https://api.example.com/data");

// Without keep-alive (default)
process.env.FETCH_KEEP_ALIVE = "false";
const standardFetch = createNodeFetch();

// Use in custom ofetch instance
import { createFetch } from "ofetch";
const nodeFetch = createFetch({
  fetch: createNodeFetch(),
  defaults: { timeout: 10000 }
});

Internal Constants and Patterns

The utilities module also defines several internal constants used for request processing:

// HTTP methods that typically include request payloads
const payloadMethods = ["PATCH", "POST", "PUT", "DELETE"];

// Content types that should be parsed as text
const textTypes = [
  "image/svg",
  "application/xml", 
  "application/xhtml",
  "application/html"
];

// Regular expression for JSON content type detection
const JSON_RE = /^application\/(?:[\w!#$%&*.^`~-]*\+)?json(;.+)?$/i;

These constants help ensure consistent behavior across different response types and request methods.

Install with Tessl CLI

npx tessl i tessl/npm-ofetch

docs

configuration.md

core-operations.md

error-handling.md

index.md

interceptors.md

utilities.md

tile.json