npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

kool-fetch

v0.3.5

Published

A 0-dependency fetch wrapper bringing axios like request and response interceptors

Downloads

353

Readme

How To Use

1. Install

npm install kool-fetch 

# or

bun install kool-fetch

# or

pnpm install kool-fetch

2. Usage

To get started, create a new kool-fetch instance

import { createKoolFetch } from "kool-fetch";

export const koolFetch = createKoolFetch({
    baseURL: "https://example.com",
    throwOnHttpError: true,
    httpErrorFactory: (response) => {
        return new Error(response.statusText);
    },
});

koolFetch.addInterceptor("request", (request) => {
    console.log(`Sending request to ${request.url}`);
    return request;
});

koolFetch.addInterceptor("response", (response) => {
    console.log(`Received response from ${response.url}`);

    if(response.status === 401) {
        // redirect to login page
    }

    return response;
});

Then you can use it like you would use the native fetch API

await koolFetch("/api/users"); // koolFetch is still under the hood a fetch function

await koolFetch("/api/users", {
    method: "POST",
    body: JSON.stringify({ name: "John Doe" }),
});

await koolFetch("/api/users/").unwrap("json") // A native fetch version with some cool extra 😉

Options

When you are initializing your kool-fetch instance you have multiple usefull options available

| Name | Description | Default | Example | | --- | --- | --- | --- | | baseURL | Base URL to use for all requests | undefined | "https://example.com" | new URL("https://example.com") | | fetch | Fetch function to use for all requests | globalThis.fetch | globalThis.fetch | | init | Request init to use for all requests | undefined | { headers: { "Authorization": "Bearer token" } } | | throwOnHttpError | Throw an error when the response is not ok | true | false | | httpErrorFactory | Factory function to create an error when the response is not ok | (response) => Error(response.statusText) | (response) => { return new Error(response.statusText) } | | retry | Retry configuration (boolean or object) | undefined | true / { retries: 3 } |

Features

Unwrap responses

Responses of requests made with kool-fetch are wrapped with a Proxy that expose an unwrap method. This method allows you to unwrap the response to a specific type, making it a shortcut to classical await response.json() or await response.arrayBuffer(), ... calls.

const koolFetch = createKoolFetch({
    throwOnHttpError: true,
    baseURL: "https://api.example.com",
});

await koolFetch("/api/users").unwrap("json"); // if the response is not ok, an error will be thrown

await koolFetch("/api/users").unwrap<{userId: number; name: string}>("json"); 

await koolFetch("/api/file").unwrap("arrayBuffer");

await koolFetch("/api/file").unwrap("text");

Safe version

Alternatively you can use the unwrapSafe method which return a golang style tuple containing the unwrapped value and the error if any

const [unwrappedValue, error] = await koolFetch("/api/users").unwrapSafe("json"); // if response is not ok, unwrappedValue will be undefined and error will contain the error

const [unwrappedValue, error] = await koolFetch("/api/users").unwrapSafe<{userId: number; name: string}>("json");

Interceptors

kool-fetch provides a simple API to attach interceptors to the request and response events.

Request interceptors

You can attach request interceptors, they can be used to modify the request before it is sent to the server

const logRequestInterceptor = (request: Request) => {
    console.log(`Sending request to ${request.url}`);
    return request;
};

const attachAuthorizationHeaderInterceptor = (request: Request) => {
    request.headers.set("Authorization", "Bearer token");
    return request;
};

koolFetch.addInterceptor("request", logRequestInterceptor);
koolFetch.addInterceptor("request", attachAuthorizationHeaderInterceptor);
// This will execute logRequestInterceptor and attachAuthorizationHeaderInterceptor in order

// You can also at any time remove an interceptor
koolFetch.removeInterceptor("request", logRequestInterceptor);

Response interceptors

You can attach response interceptors, they can be used to modify the response before it is returned to the client

const logResponseInterceptor = (response: Response) => {
    console.log(`Received response from ${response.url}`);
    return response;
};

const handleUnauthorizedResponseInterceptor = (response: Response) => {
   return new Response(JSON.stringify({ message: "The response has been modified" }), response);
};

koolFetch.addInterceptor("response", logResponseInterceptor);
koolFetch.addInterceptor("response", handleUnauthorizedResponseInterceptor);
// This will execute logResponseInterceptor and handleUnauthorizedResponseInterceptor in order

// You can also at any time remove an interceptor
koolFetch.removeInterceptor("response", logResponseInterceptor);
koolFetch.removeInterceptor("response", handleUnauthorizedResponseInterceptor);

Per-Request Interceptors

You can also add interceptors per-request using the chained API:

const response = await koolFetch("/api/users")
  .addInterceptor("request", (req) => {
    req.headers.set("Authorization", "Bearer token");
    return req;
  })
  .addInterceptor("response", (res) => {
    console.log("Response received");
    return res;
  });

// Also supports unwrap/unwrapSafe
const data = await koolFetch("/api/users")
  .addInterceptor("response", loggingInterceptor)
  .unwrap("json");

Retry

kool-fetch supports automatic retry on failure.

Global Retry

Enable retry globally when creating the instance:

const api = createKoolFetch({
  baseURL: "https://api.example.com",
  retry: true, // enables retry with defaults
  // or with custom config:
  retry: {
    retries: 3,
    delay: 1000,
    statusCodes: [500, 502, 503, 504],
    methods: ["GET", "HEAD", "OPTIONS"]
  }
});

Per-Request Retry Override

Override retry settings per-request:

// Disable retry for this specific request
await koolFetch("/flaky", { retry: false });

// Override with custom settings
await koolFetch("/flaky", { retry: { retries: 5 } });

Retry Options

| Option | Description | Default | | --- | --- | --- | | retries | Number of retry attempts | 3 | | delay | Delay between retries (ms) or function | 0 | | statusCodes | HTTP status codes to retry | [500, 502, 503, 504] | | methods | HTTP methods to retry | ["GET", "HEAD", "OPTIONS"] |

Retry Delay Function

The delay option can be a function for dynamic delays (e.g., exponential backoff):

retry: {
  retries: 3,
  delay: (attempt, response) => {
    // Use Retry-After header if available
    const retryAfter = response?.headers.get("Retry-After");
    if (retryAfter) return parseInt(retryAfter) * 1000;
    
    // Exponential backoff
    return 1000 * 2 ** attempt;
  }
}

Usage with third party libraries

Hono RPC

You can directly use kool-fetch with @honojs RPC client since it is a drop in replacement for the native fetch API

import { createKoolFetch } from "kool-fetch";
import { hc } from "hono/client";

const koolFetch = createKoolFetch({
    init: { credentials: "include" }
});

koolFetch.addInterceptor("response", async (response) => {
    if (!response.ok) {
        const clonedResponse = response.clone();
        const body = await clonedResponse.json()
        toast.error(body.message ?? "An unknown error occurred");
    }

    return response;
});

export const apiClient = hc(import.meta.env.VITE_API_URL, { fetch: koolFetch });

License

MIT


GitHub @Thomascogez  ·  X @Thomascogez