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

hyperttp

v0.5.5

Published

A high-performance, universal HTTP client for Node.js, Bun, Deno and Browser with caching, retries, queueing, rate limiting, cookies and logging.

Readme

Hyperttp

An Axios-like HTTP client with built-in request policies for modern TypeScript applications.

English | Русский

npm version npm downloads license typescript

Hyperttp keeps familiar get, post, put, patch, and delete methods while adding response parsing, caching, in-flight deduplication, rate limiting, concurrency queues, and interceptors through one client.

import { HyperClient } from "hyperttp";

const http = new HyperClient({
  baseURL: "https://api.example.com",
});

const users = await http.get<User[]>("/users");

console.log(users);

Shortcuts return parsed data directly. No response.data step is required.

Why Hyperttp?

A real API client often grows beyond fetch() or a basic request wrapper:

HTTP request
├── serialization and parsing
├── caching
├── duplicate request prevention
├── rate limiting
├── concurrency control
├── cancellation and timeouts
└── interceptors

Hyperttp provides these policies through one composable client. It is most useful for SDKs, backend services, bots, CLI tools, API integrations, and applications that would otherwise build and maintain this infrastructure themselves.

Hyperttp is not universally better than every HTTP client. If you only need a small Fetch wrapper, Ky or ofetch may be a better fit. For a Node-only client with HTTP/2 and pagination, Got is more specialized. See Choosing a client for an objective comparison.

Installation

Node.js usage requires Node.js 22.19.0 or newer because Hyperttp uses Undici 8.

npm install hyperttp
pnpm add hyperttp
bun add hyperttp
deno add npm:hyperttp

Quick start

Create a reusable client

import { HyperClient } from "hyperttp";

export const api = new HyperClient({
  baseURL: "https://api.example.com/v1/",
});

Reuse the client across your application so transports, connection pools, cache entries, and plugin state can be shared.

interface User {
  id: string;
  name: string;
  active: boolean;
}

const users = await api.get<User[]>("users");
const user = await api.get<User>("users/42");

Send data

const created = await api.post<User>("users", {
  name: "Alice",
  active: true,
});

await api.put("users/42", {
  name: "Alice Cooper",
});

await api.patch("users/42", {
  active: false,
});

await api.delete("users/42");

Objects and arrays are serialized as JSON by the built-in serializer.

Request options

Query parameters

Hyperttp uses query where Axios uses params:

const users = await api.get<User[]>("users", {
  query: {
    page: 1,
    limit: 20,
    active: true,
    role: ["admin", "editor"],
  },
});

Array values become repeated query parameters. null and undefined values are omitted by the fluent builder.

Headers

const profile = await api.get<User>("profile", {
  headers: {
    Authorization: `Bearer ${token}`,
    "X-Request-ID": requestId,
  },
});

Hyperttp currently has no top-level default headers option. Use request options or a request interceptor when headers must be added globally.

Timeout and cancellation

const controller = new AbortController();

const request = api.get("reports/large", {
  timeout: 10_000,
  signal: controller.signal,
});

controller.abort();

await request;

Timeouts, aborts, and transport failures reject the request. HTTP error status codes are handled differently; see Responses and HTTP errors.

Explicit response format

const text = await api.get<string>("health", "text");

const document = await api.get("document.xml", {
  responseType: "xml",
});

Supported response hints are json, text, xml, html, buffer, and stream.

Understanding baseURL

Hyperttp resolves relative URLs with the standard URL rules:

const api = new HyperClient({
  baseURL: "https://api.example.com/v1/",
});

await api.get("users"); // https://api.example.com/v1/users
await api.get("/users"); // https://api.example.com/users

A leading slash starts from the origin root. Omit it when the path should remain under the baseURL pathname.

baseURL applies to HyperClient shortcuts, head(), stream(), and RequestBuilder. The directly delegated client.rest namespace currently expects an absolute URL.

Fluent requests

Use shortcuts for simple requests and RequestBuilder when a longer request reads better as a chain:

const user = await api
  .request("users")
  .post()
  .headers({
    "X-Request-ID": requestId,
  })
  .query({ notify: true })
  .jsonBody({
    name: "Alice",
    active: true,
  })
  .json()
  .send<User>();

Available builder methods include:

  • HTTP method: get, post, put, patch, delete, head, options, method
  • Request data: headers, query, body, jsonBody
  • Control: timeout, signal
  • Response hint: json, text, xml, html, buffer, stream
  • Utility: clone, send

For live response streaming, use client.stream() rather than the builder response hint.

Responses and HTTP errors

Convenience methods return data

const users = await api.get<User[]>("users");

The return value is already the parsed response data. Status, headers, and other envelope fields are intentionally omitted from shortcut results.

Access the full response

Use the REST namespace when status and headers are needed:

const response = await api.rest.get<User[]>("https://api.example.com/v1/users");

console.log(response.ok);
console.log(response.status);
console.log(response.headers);
console.log(response.data);

The full response contains protocol, ok, status, statusText, headers, url, data, and optional metadata/raw fields.

HTTP statuses do not throw by default

A 4xx or 5xx response resolves normally. With shortcuts, the parsed error body is returned. With the full response API, inspect response.ok or response.status:

const response = await api.rest.get<ErrorPayload>("https://api.example.com/v1/users/missing");

if (!response.ok) {
  console.error(response.status, response.data);
}

This differs from Axios, Ky, Got, and ofetch defaults, which commonly reject non-success responses.

Built-in request policies

Built-in plugins are registered automatically. Policies that should be explicit, such as rate limiting and queueing, can be enabled in client configuration:

const api = new HyperClient({
  baseURL: "https://api.example.com/v1/",

  cache: {
    enabled: true,
    ttl: 60_000,
    maxSize: 1_000,
  },

  inflight: {
    enabled: true,
  },

  rateLimit: {
    enabled: true,
    maxRequests: 100,
    windowMs: 60_000,
  },

  queue: {
    enabled: true,
    maxConcurrent: 20,
  },
});

| Policy | Purpose | | ----------------------- | ------------------------------------------------------------- | | Cache | Reuse successful GET/HEAD responses according to cache policy | | In-flight deduplication | Share compatible concurrent GET requests | | Rate limiter | Control request volume and react to server penalties | | Queue | Limit concurrent work per configured partition | | Serializer | Encode object/array request bodies | | Parser | Convert JSON, text, HTML, XML, buffer, and empty responses | | Interceptors | Transform requests and responses |

Disable the entire built-in plugin preset when composing a custom pipeline:

const client = new HyperClient({
  builtInPlugins: false,
  plugins: [myPlugin],
});

The REST protocol and plugins explicitly supplied through plugins or client.use() remain available.

Retry options exist in the current type surface, but retry execution is not documented as a stable feature in 0.5.x. It is therefore not counted as an active capability in the comparison below.

Streaming

stream() returns the complete response envelope. Its data is an async-iterable byte stream:

const response = await api.stream("events");
const decoder = new TextDecoder();

for await (const chunk of response.data) {
  console.log(decoder.decode(chunk, { stream: true }));
}

The concrete stream implementation depends on the active runtime transport.

TypeScript

Public request, response, client, and transport types are exported from the package root:

import {
  HyperClient,
  type HyperClientOptions,
  type RestRequestOptions,
  type UniversalResponse,
} from "hyperttp";

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

const options: RestRequestOptions = {
  query: { active: true },
  timeout: 5_000,
};

const users = await new HyperClient({
  baseURL: "https://api.example.com",
}).get<User[]>("/users", options);

Generics describe the expected response shape; they do not validate untrusted server data at runtime.

Lifecycle

A client may own connection pools and other transport resources. Destroy long-lived clients during application shutdown, not after every request:

const api = new HyperClient({
  baseURL: "https://api.example.com",
});

try {
  await runApplication(api);
} finally {
  await api.destroy(true);
}

Pass true for graceful shutdown or false when resources must be destroyed immediately.

Coming from Axios?

The request style is intentionally familiar, but Hyperttp is not a drop-in Axios replacement.

| Axios | Hyperttp | | ------------------------------- | ------------------------------------------------------------- | | axios.create({ baseURL }) | new HyperClient({ baseURL }) | | axios.get(url, { params }) | client.get(url, { query }) | | axios.post(url, data, config) | client.post(url, body, options) | | response.data | Returned directly by shortcut methods | | response.status | Use an absolute URL with client.rest.* | | signal | signal in request options or the positional signal argument | | Request/response interceptors | Built-in interceptor plugin or custom plugins | | Instance default headers | Request options or an interceptor |

Legacy Request and PreparedRequest exports remain available for compatibility but are deprecated. New code should use client.request() and RequestBuilder.

Choosing a client

The goal of this table is not to declare a universal winner. It highlights when each client is a natural fit based on its documented built-in features.

| Capability | Fetch | Axios | Ky | ofetch | Got | Hyperttp | | -------------------------- | --------------- | ------------------------- | ------------------------ | -------------------------- | -------------------------------- | ------------------------------ | | Primary runtimes | Runtime native | Browser, Node | Browser, Node, Bun, Deno | Node, browser, workers | Node | Node plus Fetch-based runtimes | | Parsed-data shortcut | No | Via response.data | Via .json() | Yes | JSON mode | Yes | | Full response access | Yes | Yes | Yes | Yes (.raw) | Yes | Yes (client.rest) | | Automatic retries | No | No built-in | Yes | Yes | Yes | Not currently stable | | Application response cache | No | No built-in | No built-in | No built-in | Yes | Yes | | In-flight deduplication | No | No built-in | No built-in | No built-in | No built-in | Yes | | Built-in rate limiting | No | No | No | No | No | Yes | | Built-in concurrency queue | No | No | No | No | No | Yes | | Interceptors or hooks | No | Interceptors | Hooks | Interceptors | Hooks | Interceptors and plugins | | Notable strength | Zero dependency | Ecosystem and familiarity | Small Fetch-based API | Universal Fetch experience | Node streams, HTTP/2, pagination | Integrated request policies |

Choose Fetch when

  • you need no dependency;
  • platform APIs are enough;
  • you are comfortable composing policies yourself.

Choose Axios when

  • your team already knows its API;
  • its mature ecosystem matters more than built-in operational policies;
  • browser and Node support are the main targets.

Choose Ky when

  • you want a small, modern Fetch wrapper;
  • retries and lifecycle hooks cover your needs;
  • a Fetch-compatible response model is desirable.

Choose ofetch when

  • you want automatic parsing and retries with a Fetch-oriented universal API;
  • Node, browser, and worker portability matters;
  • you do not need cache, deduplication, rate limiting, and queueing in the client itself.

Choose Got when

  • the application is Node-only;
  • advanced streams, HTTP/2, pagination, timings, and RFC caching are important.

Choose Hyperttp when

  • you want Axios-like request methods that return parsed data directly;
  • cache, deduplication, rate limiting, and concurrency control should share one pipeline;
  • the client is part of an SDK, service, bot, CLI, scraper, or integration with operational limits;
  • you prefer one extensible client over several unrelated wrappers.

Comparison references: Axios, Ky, ofetch, and Got. Features and runtime support can change between releases; verify the requirements that matter to your application.

Ky, Axios, ofetch, and Got are comparison references only. They are not Hyperttp dependencies.

Runtime transports

  • Node.js resolves the included @hyperttp/transport-undici transport.
  • Other runtimes can use the Fetch fallback when globalThis.fetch is available.
  • Optional Bun- and Deno-specific transport packages may be installed separately.
  • A custom transport can be passed to the constructor or through customTransport.

Use await client.getTransportName() to inspect the selected transport.

Runtime code paths target Node.js, Bun, Deno, and browsers. The repository test suite currently verifies the Node.js path; validate the other runtimes in your own deployment environment.

Ecosystem

Hyperttp is composed from focused packages:

  • @hyperttp/core
  • @hyperttp/types
  • @hyperttp/interceptors
  • @hyperttp/serializer
  • @hyperttp/metrics
  • @hyperttp/inflight
  • @hyperttp/cache
  • @hyperttp/ratelimit
  • @hyperttp/queue
  • @hyperttp/parser
  • @hyperttp/transport-undici

License

MIT