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

@m0n0lab/http-client

v0.2.2

Published

[![npm version](https://img.shields.io/npm/v/@m0n0lab/http-client.svg)](https://www.npmjs.com/package/@m0n0lab/http-client) [![http-client coverage](https://codecov.io/gh/pabloimrik17/monolab/badge.svg?flag=http-client)](https://codecov.io/gh/pabloimrik17

Readme

@m0n0lab/http-client

npm version http-client coverage http-client bundle

Type-safe HTTP client contracts for web and Node.js environments.

Overview

This package provides TypeScript interfaces and types that define the contract for HTTP client implementations. It enables:

  • Abstraction over implementation: Write code once, swap HTTP libraries (axios, ky) without changes
  • Type safety: Full generic support for request/response/error types
  • Extensibility: Interceptors, retry hooks, and cache plugins for customization
  • Familiar patterns: onFulfilled/onRejected pattern mirrors Promise.then() for easy adoption

Note: This is a contract-only package (pure TypeScript types and interfaces). Concrete implementations (axios adapter, ky adapter) are provided in separate packages.

Installation

npm

npm install @m0n0lab/http-client

pnpm

pnpm add @m0n0lab/http-client

JSR

npx jsr add @m0n0lab/http-client

Contracts

The package defines interfaces for all aspects of HTTP communication:

Core Interfaces

  • HttpClient - Main client interface with HTTP methods (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS)
  • HttpRequestConfig - Request configuration with headers, timeout, retry, cache, etc.
  • HttpResponse<T> - Typed response with data, status, headers, and metadata
  • HttpClientFactory - Factory function for creating client instances

Error Hierarchy

  • HttpError - Base error class
  • HttpNetworkError - Network-level failures (connection refused, DNS, timeout)
  • HttpResponseError<T> - HTTP error responses with typed data
  • Specific error classes: HttpBadRequestError, HttpUnauthorizedError, HttpNotFoundError, etc.

Advanced Features

  • Interceptors: Transform requests/responses using onFulfilled/onRejected pattern
  • Retry: Configurable retry logic with exponential/linear/jitter backoff
  • Deduplication: Prevent redundant concurrent requests
  • Cache: Pluggable cache layer with stale-while-revalidate support

Usage Examples

Creating a Typed Client

import type { HttpClient, HttpClientFactory } from "@m0n0lab/http-client";

interface User {
    id: number;
    name: string;
    email: string;
}

interface CreateUserDto {
    name: string;
    email: string;
}

// Assuming you have an axios or ky adapter
declare const createHttpClient: HttpClientFactory;

const client: HttpClient = createHttpClient({
    baseUrl: "https://api.example.com",
    timeout: 5000,
    headers: {
        "Content-Type": "application/json",
    },
});

// GET with type inference
const response = await client.get<User>("/users/1");
console.log(response.data.name); // Type-safe access

// POST with typed body
const newUser = await client.post<User, CreateUserDto>("/users", {
    name: "Alice",
    email: "[email protected]",
});

// PATCH with partial update
await client.patch<User, Partial<User>>("/users/1", {
    name: "Bob",
});

// DELETE
await client.delete("/users/1");

Error Handling with Typed Errors

import {
    HttpError,
    HttpNetworkError,
    HttpUnauthorizedError,
    HttpNotFoundError,
    HttpResponseError,
} from "@m0n0lab/http-client";

try {
    const user = await client.get<User>("/users/123");
    console.log(user.data);
} catch (error) {
    if (error instanceof HttpNetworkError) {
        console.error("Network failure:", error.code);
        // Handle connection issues
    } else if (error instanceof HttpUnauthorizedError) {
        console.error("Authentication required");
        // Redirect to login
    } else if (error instanceof HttpNotFoundError) {
        console.error("User not found");
        // Show 404 page
    } else if (error instanceof HttpResponseError) {
        console.error("Server error:", error.status, error.data);
        // Handle other HTTP errors
    }
}

Interceptor Usage

import type { RequestOnFulfilled, ResponseOnRejected } from "@m0n0lab/http-client";

// Add authentication to all requests
const authInterceptor: RequestOnFulfilled = async (config) => ({
    ...config,
    headers: {
        ...config.headers,
        Authorization: `Bearer ${await getAuthToken()}`,
    },
});

client.addRequestInterceptor(authInterceptor);

// Handle 401 errors by refreshing token
const refreshInterceptor: ResponseOnRejected = async (error) => {
    if (error instanceof HttpUnauthorizedError) {
        await refreshAuthToken();
        return client.request(error.request); // Retry original request
    }
    throw error;
};

client.addResponseInterceptor((response) => response, refreshInterceptor);

// Remove interceptor when done
const handle = client.addRequestInterceptor(authInterceptor);
client.removeInterceptor(handle);

Retry Configuration

import { exponentialBackoff } from "@m0n0lab/http-client";

declare const createHttpClient: HttpClientFactory;

const client = createHttpClient({
    baseUrl: "https://api.example.com",
    retry: {
        attempts: 3,
        delay: exponentialBackoff(1000, 10000), // Start at 1s, max 10s
        condition: (error) => {
            // Retry on network errors and 5xx server errors
            return (
                error instanceof HttpNetworkError ||
                (error instanceof HttpResponseError && error.status >= 500)
            );
        },
        onRetry: (error, attempt) => {
            console.log(`Retrying request (attempt ${attempt})`);
        },
        respectRetryAfter: true, // Honor server's Retry-After header
    },
});

Cache Configuration

import type { HttpCache, CacheEntry } from "@m0n0lab/http-client";

// Implement custom cache backend
class MemoryCache implements HttpCache {
    private store = new Map<string, CacheEntry>();

    async get(key: string): Promise<CacheEntry | null> {
        const entry = this.store.get(key);
        if (!entry) return null;

        // Check if expired
        if (Date.now() - entry.timestamp > entry.ttl) {
            this.store.delete(key);
            return null;
        }

        return entry;
    }

    async set(key: string, value: CacheEntry): Promise<void> {
        this.store.set(key, value);
    }

    async delete(key: string): Promise<void> {
        this.store.delete(key);
    }

    async clear(): Promise<void> {
        this.store.clear();
    }
}

declare const createHttpClient: HttpClientFactory;

const client = createHttpClient({
    baseUrl: "https://api.example.com",
    cache: {
        cache: new MemoryCache(),
        ttl: 60000, // 1 minute
        respectCacheHeaders: true,
        staleWhileRevalidate: true, // Return stale data while refreshing
        invalidatePatterns: (config) => {
            // Invalidate user cache after mutations
            if (config.baseUrl?.includes("/users")) {
                return ["/users/*"];
            }
            return [];
        },
    },
});

Migration from Raw axios/fetch

Before (axios)

import axios from "axios";

const response = await axios.get("https://api.example.com/users/1");
const user = response.data;

After (with adapter)

import { createAxiosHttpClient } from "@m0n0lab/http-client-axios";

const client = createAxiosHttpClient({
    baseUrl: "https://api.example.com",
});

const response = await client.get<User>("/users/1");
const user = response.data; // Type-safe!

Roadmap

  • ✅ Package foundation and infrastructure
  • ✅ Core HTTP client contracts (types and interfaces)
  • 🚧 Axios adapter implementation
  • 🚧 Ky adapter implementation
  • 🚧 neverthrow (ResultAsync) wrapper for functional error handling
  • 🚧 effect-ts integration for advanced effect systems

Features

  • 🎯 Clean abstraction over popular HTTP libraries (axios, ky)
  • ✅ Fully typed with TypeScript
  • 🔄 Pluggable HTTP client implementations
  • 🛡️ Type-safe error handling
  • ⚡ Interceptors with onFulfilled/onRejected pattern
  • 🔁 Configurable retry with backoff strategies
  • 🚫 Request deduplication
  • 💾 Pluggable cache layer
  • 🌐 Works in browser and Node.js environments
  • 📦 Zero runtime dependencies (pure types)
  • 🧪 Comprehensive type-level tests
  • 📘 Well documented

License

MIT