Skip to content

tada5hi/hapic

Repository files navigation

hapic

hapic

The tiny, fetch-based HTTP API client.
Request & response hooks, transformers, authorization helpers and proxy support —
plus a family of typed service clients built on the same core.

CI npm version Known Vulnerabilities Conventional Commits License

Documentation · Getting Started · Guide · Packages


Why hapic?

Most HTTP libraries sit at one of two extremes: a bare fetch wrapper where you re-solve auth, JSON handling and error throwing on every project — or a heavyweight SDK locked to a single backend. hapic splits the difference. A tiny base Client owns the transport — and typed service clients extend it for specific backends.

// the base client — fetch with batteries
import hapic from 'hapic';

const { data } = await hapic.post('https://api.example.com/users', { name: 'Max' });

// a typed service client built on that same core
import { HarborClient } from '@hapic/harbor';

const harbor = new HarborClient({ connectionString: 'admin:pw@https://registry.example.com/api/v2.0/' });
const project = await harbor.project.getOne(1);   // typed call → no URL building, no manual decoding

That split serves two audiences:

  • App developers get fetch with batteries included — baseURL, authorization headers, JSON in/out, errors that throw on non-2xx, proxy support, and hooks for retry/refresh-token flows — in a package small enough to drop into the browser, Node.js or a worker.
  • Service-client authors get a transport contract: extend Client, declare a few domain *API classes, and inherit the entire request lifecycle — hooks, transformers, error handling and cross-environment fetch — instead of re-implementing it per backend. The @hapic/* clients in this repo are exactly that pattern, shipped.

Features

  • Simple APIget / post / put / patch / delete / head shortcuts over a single request().
  • 🛑 Hooks to intercept and mutate requests, responses and errors — error hooks can even recover a failed request (retry / refresh token).
  • 🔄 Transformers for request payloads & headers.
  • 🔐 Authorization helpersBasic, Bearer and api-key headers from a typed config.
  • Throws on non-2xx — every 400–599 response becomes a typed HttpResponseError; network failures become a NetworkError.
  • 🎭 Proxy support out of the box.
  • 🌐 Runs everywhere — Node.js, the browser and worker environments, through one cross-env fetch layer.
  • 🧩 Typed service clients for Harbor, Loki, OAuth2, Vault and VictoriaLogs — all on the same core.

Getting Started

npm install hapic
import hapic, { createClient } from 'hapic';

// use the default singleton instance...
const response = await hapic.post('https://api.example.com/users', {
    firstName: 'Max',
    lastName: 'Mustermann',
});

console.log(response);
// { data: …, headers: …, status: …, statusText: … }

// ...or create a configured client
const client = createClient({
    baseURL: 'https://api.example.com/',
});

const { data } = await client.get('users/1');

Follow the full walkthrough at hapic.tada5hi.net/getting-started.

The request lifecycle

Every request(config) runs through the same pipeline. Hooks and transformers slot into it, so cross-cutting concerns live in one place instead of at every call site:

request(config)
  1. merge headers over the client defaults
  2. resolve URL          baseURL + params/query
  3. request hooks    ←   intercept / mutate the outgoing request
  4. transformers     ←   reshape the body
  5. dispatch             fetch(url, init)  (with optional proxy)
  6. decode               by responseType, or detected from Content-Type
  7. throw on 4xx–5xx ←   error hooks may recover: return a Response, or retry
  8. response hooks   ←   inspect / transform the result
import { createClient, HookName } from 'hapic';

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

// attach a bearer token on every request
client.on(HookName.REQUEST, (request) => ({
    ...request,
    headers: { ...request.headers, authorization: `Bearer ${token}` },
}));

// recover from a 401 by refreshing the token and retrying
client.on(HookName.REQUEST_ERROR, async (error) => {
    if (error.response?.status === 401) {
        token = await refreshToken();
        return error.request;   // returning RequestOptions retries the request
    }
});

→ Deep dive: The Client · Hooks · Transformers · Error Handling · Building a Service Client

Packages

Base

Package Version Description
hapic npm Base fetch-based HTTP client — request methods, hooks, transformers, headers, authorization, proxy and auto-throwing errors. The foundation every other package builds on.

Service Clients

Package Version Description
@hapic/harbor npm Typed client for the Harbor container registry — projects, repositories, artifacts, robots, webhooks and more.
@hapic/loki npm Client for Grafana Loki — push, query and analyze logs through the querier, distributor and compactor endpoints.
@hapic/oauth2 npm OAuth2 / OpenID Connect client — authorization flows, token issuance, userinfo and provider metadata.
@hapic/vault npm Client for HashiCorp Vault — key-value engines (v1/v2), mounts and secrets management.
@hapic/victorialogs npm Client for VictoriaLogs — ingest and query logs via its HTTP API.

Every service client follows the same shape — a <Service>Client extends Client composing domain *API classes — so once you know one, you know them all. See Building a Service Client to publish your own.

Contributing

npm ci             # install
npm run build      # build all packages (Nx, cached)
npm run test       # run tests
npm run lint       # ESLint — must pass with zero errors

Commits follow Conventional Commits; releases and changelogs are automated via release-please.

License

Made with 💚

Published under MIT License.

About

A tiny & simple fetch based http client with a collection of different presets.

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors