Skip to content

HTTP

Native-fetch HTTP for TypeScript apps that you actually enjoy writing. No Axios. No magic globals. One tiny runtime dep. Errors are typed — not stringly-typed exceptions — so handling a 404 looks like if (error.isNotFound) instead of parsing a status code out of a thrown object.

You can be making requests within ten seconds of yarn adding it, because the package ships a pre-built http instance you can import and call. When you outgrow that — when you need a base URL, an auth header, a retry policy — you graduate to your own configured instance without changing a single call site.

Highlighted features

Ready-to-go singleton

Import http and start calling http.get(url) — no setup, no config, no boilerplate. Outgrow it later without changing call sites.

Typed data + error results

Every call returns a discriminated union — data on success, error on failure. No try/catch, no casts, no thrown values mid-render. TypeScript narrows the happy path for you.

Cancellation built in

Every returned promise carries a .cancel() method, or pass an external AbortSignal. Cancelled requests resolve with error.isAborted = true — no rejected-promise noise.

Smart GET deduplication + retry

Two concurrent calls to the same URL share one underlying fetch. Retry with configurable backoff and optional jitter — opt in per request or per instance.

Cache + interceptors

Plug any CacheDriver-compatible store for GET caching. before() shapes outgoing requests; after() runs on both success and error results — perfect for global toasts, refresh-token flows, and logging.

Resource for RESTful CRUD

Subclass Resource, set endpoint = “users”, and you get typed list / get / create / update / delete / publish — zero boilerplate, full type inference end to end.

Install

Terminal window
npm install @mongez/http

Runs anywhere fetch, AbortController, and TextDecoder are available — modern browsers, Node 18+, Bun, Deno. The only runtime dep is @mongez/concat-route for path joining.

Quick peek

import { http } from "@mongez/http";
const { data, error } = await http.get<User[]>(
"https://api.example.com/users",
);
if (error) {
console.error(error.message);
return;
}
// `data` is `User[]` here — TypeScript narrows automatically.
console.log(data.length, "users");

The package ships a ready-to-use http singleton — import it and call it, no new, no config. The {data, error} result type means HTTP failures don’t throw. Typed error predicates like error.isNotFound / error.isUnauthorized / error.isValidationError are documented in error handling.

When you’re ready: bootstrap your own instance

Most apps want a base URL, an auth header, maybe a retry policy. Create one configured instance and reuse it everywhere:

src/lib/http.ts
import { Http, setCurrentHttp } from "@mongez/http";
export const http = new Http({
baseURL: import.meta.env.VITE_API_URL,
auth: () => {
const token = localStorage.getItem("token");
return token ? `Bearer ${token}` : null;
},
});
setCurrentHttp(http); // lets Resource classes pick it up lazily
// anywhere else in your app
import { http } from "./lib/http";
const { data } = await http.get<User[]>("/users"); // hits VITE_API_URL/users

Need a one-off variant? Don’t new Http() again — extend() returns a fresh instance with merged config:

const adminHttp = http.extend({ baseURL: "https://admin.api.com" });

Where to go next

  • HTTP client — every request method, options, and config field
  • Error handling — the HttpError predicate cheat sheet
  • Resource — RESTful CRUD subclasses
  • Caching — GET cache configuration and drivers
  • Interceptorsbefore() / after() and lifecycle events
  • Streaming — SSE, NDJSON, and raw streams
  • Recipes — file uploads, React Query, multi-tenant clients