Skip to content

HTTP API client for TypeScript

fetch, with a pipeline.

Hooks, transformers and typed errors around every request. Error hooks can recover or retry. One tiny client for Node, browsers and workers.

Get started

MIT licensed · ESM only · Node, browser and workers · built on fetch

error hook returned new options, request ran again

Documentation

Pick your way in

Hooks and recovery

Hooks that can rewrite the outcome

hapic runs your hooks around the whole request lifecycle. An error hook is special. Return a Response to swallow the failure, or fresh RequestOptions to retry, and the client runs the request again for you. Token refresh, backoff and fallbacks become a handful of lines.

  • requestchange options, inject headers or add transformers before dispatch
  • responsenormalize or unwrap the payload of every 2xx and 3xx response
  • requestError and responseErrorrecover with a Response, or retry with new options
  • on() and off()register many hooks, remove one by id or all of them by name
Read the hooks guide
// auth.ts
import { createClient, isClientErrorWithStatusCode } from 'hapic';

const client = createClient({ baseURL: 'https://api.example.com/' });

// Recover from a 401 by refreshing the token, then retry
client.on('responseError', async (error) => {
    if (isClientErrorWithStatusCode(error, 401)) {
        const token = await refreshAccessToken();

        const headers = new Headers(error.request.headers);
        headers.set('authorization', `Bearer ${token}`);

        // returning RequestOptions runs the original request again
        return { ...error.request, headers };
    }

    // anything else keeps throwing
    throw error;
});

Features

One client, batteries included

A small, predictable core with everything a real HTTP layer needs already wired in.

  • Tiny, built on fetch

    A thin wrapper over the platform fetch, with a base URL and shortcuts for get, post, put, patch, delete and head.

  • Transformers

    Shape the outgoing body with transform and the decoded data with responseTransform, per request or for the whole client.

  • Errors that throw

    Any 4xx or 5xx response throws a typed error carrying the request, the response and its status. Guards like isClientError work across bundled copies.

  • Timeouts and abort

    Set a timeout per request or per client and combine it with your own abort signal. A timed out request fails with the code ETIMEDOUT.

  • Runs everywhere

    One code path for Node, the browser and worker runtimes. fetch, Headers, Blob and FormData resolve per environment.

  • Testable

    Pass a MemoryTransport to the client. It records every request and answers it with your handler, while hooks and decoding run as usual.

From zero to request

From install to first request

No client builders, no interceptor registries. A function call and a few options.

npm install hapic

The family

A client for the services you already run

Each package extends the same Client with a typed API for one service.

@hapic/harbor

Typed client for the Harbor container registry: projects, repositories, artifacts, robots and webhooks.

@hapic/oauth2

OAuth2 and OpenID Connect client: authorization URLs, every token grant, device authorization, introspection and userinfo.

@hapic/vault

HashiCorp Vault client: key value v1 and v2 secret engines plus mount management.

@hapic/loki

Grafana Loki client: push log streams and run LogQL instant and range queries.