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.
Documentation · Getting Started · Guide · Packages
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 decodingThat split serves two audiences:
- App developers get
fetchwith 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*APIclasses, and inherit the entire request lifecycle — hooks, transformers, error handling and cross-environmentfetch— instead of re-implementing it per backend. The@hapic/*clients in this repo are exactly that pattern, shipped.
- ✨ Simple API —
get/post/put/patch/delete/headshortcuts over a singlerequest(). - 🛑 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 helpers —
Basic,Bearerandapi-keyheaders from a typed config. - ❌ Throws on non-2xx — every
400–599response becomes a typedHttpResponseError; network failures become aNetworkError. - 🎭 Proxy support out of the box.
- 🌐 Runs everywhere — Node.js, the browser and worker environments, through one cross-env
fetchlayer. - 🧩 Typed service clients for Harbor, Loki, OAuth2, Vault and VictoriaLogs — all on the same core.
npm install hapicimport 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.
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
| Package | Version | Description |
|---|---|---|
hapic |
Base fetch-based HTTP client — request methods, hooks, transformers, headers, authorization, proxy and auto-throwing errors. The foundation every other package builds on. |
| Package | Version | Description |
|---|---|---|
@hapic/harbor |
Typed client for the Harbor container registry — projects, repositories, artifacts, robots, webhooks and more. | |
@hapic/loki |
Client for Grafana Loki — push, query and analyze logs through the querier, distributor and compactor endpoints. | |
@hapic/oauth2 |
OAuth2 / OpenID Connect client — authorization flows, token issuance, userinfo and provider metadata. | |
@hapic/vault |
Client for HashiCorp Vault — key-value engines (v1/v2), mounts and secrets management. | |
@hapic/victorialogs |
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.
npm ci # install
npm run build # build all packages (Nx, cached)
npm run test # run tests
npm run lint # ESLint — must pass with zero errorsCommits follow Conventional Commits; releases and changelogs are automated via release-please.
Made with 💚
Published under MIT License.