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 batteriesimporthapicfrom'hapic';const{ data }=awaithapic.post('https://api.example.com/users',{name: 'Max'});// a typed service client built on that same coreimport{HarborClient}from'@hapic/harbor';constharbor=newHarborClient({connectionString: 'admin:pw@https://registry.example.com/api/v2.0/'});constproject=awaitharbor.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 hapicimporthapic,{createClient}from'hapic';// use the default singleton instance...constresponse=awaithapic.post('https://api.example.com/users',{firstName: 'Max',lastName: 'Mustermann',});console.log(response);// { data: …, headers: …, status: …, statusText: … }// ...or create a configured clientconstclient=createClient({baseURL: 'https://api.example.com/',});const{ data }=awaitclient.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';constclient=createClient({baseURL: 'https://api.example.com/'});// attach a bearer token on every requestclient.on(HookName.REQUEST,(request)=>({
...request,headers: { ...request.headers,authorization: `Bearer ${token}`},}));// recover from a 401 by refreshing the token and retryingclient.on(HookName.REQUEST_ERROR,async(error)=>{if(error.response?.status===401){token=awaitrefreshToken();returnerror.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.