bbos-httpreq
v1.0.1
Published
Lightweight, fast, fetch-based drop-in replacement for axios with extra features (retry, dedupe, cache)
Maintainers
Readme
bbos-httpreq
Lightweight, fast, fetch-based HTTP client for React, Next.js and Node.
Zero dependencies · Fully type-safe (no any) · Works everywhere fetch does · Built on the modern Web Platform.
import httpReq from 'bbos-httpreq';
type User = { id: string; name: string };
const { data } = await httpReq.get<User[]>('/users', { params: { page: 1 } });
// ^? User[] — fully typed, no as anyWhy bbos-httpreq?
You need to talk to a server: get users, send a form, upload a file, handle auth. You can use fetch directly, but you end up writing the same helpers again and again.
bbos-httpreq gives you those helpers — already tested, already type-safe — in under 8kB gz.
| You need | With fetch | With bbos-httpreq |
|---|---|---|
| Add auth token to every request | Write interceptor yourself | api.interceptors.request.use(...) — once |
| Query ?tags[]=1&tags[]=2 | Build string yourself | params: { tags: [1,2] } |
| Stop after 5s | Make AbortSignal yourself | timeout: 5000 |
| Try again if busy | Write a loop | retry: 2 |
| Don't send same search twice | Add logic | dedupe: true |
| Remember config for 30s | Add cache | cache: { ttl: 30_000 } |
Features
- Familiar API —
httpReq.get/post/put/patch/delete/postForm,httpReq.create(),httpReq.getUri() - Interceptors —
request/responsewitheject,clear,synchronous,runWhen - Headers —
HttpReqHeaders(case-insensitive,get/set/has/delete/clear,parseParameters) - Data — JSON auto-stringify/parse,
FormData,URLSearchParams,Blob,ArrayBuffer,stream - Helpers —
toFormData/formToJSON,mergeConfig,getAdapter - Cancel —
AbortSignal(preferred) +CancelTokencompat,isCancel - Errors —
HttpReqErrorwithcode/status/cause,isHttpReqError,redactfor logs - Extras —
retry(exponential +Retry-After),dedupe(coalesce),cache(memory, TTL),fetchOptionspassthrough for Next.js ISR
Install
npm i bbos-httpreq
# pnpm add bbos-httpreq
# yarn add bbos-httpreqRequires Node 18+ (native fetch) or a modern browser. Works in Cloudflare Workers, Vercel Edge, Bun, Deno.
Quick start
import httpReq from 'bbos-httpreq';
// 1. Simple GET with query
const { data: users } = await httpReq.get('/users', {
params: { page: 1, role: 'admin' },
});
// 2. Type safe — tell TypeScript what the server returns
type User = { id: string; name: string; email: string };
const { data: user } = await httpReq.get<User>('/users/1');
// user.name is string — TypeScript checks it
// 3. Create an instance for your API
export const api = httpReq.create({
baseURL: 'https://api.example.com',
timeout: 10_000,
headers: { Accept: 'application/json' },
});
await api.get('/users'); // -> https://api.example.com/users
await api.post('/users', { name: 'Ada' }); // JSON autoReact example
import { useEffect, useState } from 'react';
import httpReq, { isCancel } from 'bbos-httpreq';
export function useUsers() {
const [data, setData] = useState(null);
useEffect(() => {
const ctrl = new AbortController();
httpReq.get('/api/users', { signal: ctrl.signal, cache: true })
.then(r => setData(r.data))
.catch((e: unknown) => { if (!isCancel(e)) throw e; });
return () => ctrl.abort();
}, []);
return data;
}Next.js example
// app/products/page.tsx — Server Component
import httpReq from 'bbos-httpreq';
export default async function Page() {
const { data } = await httpReq.get('https://api.example.com/products', {
fetchOptions: { next: { revalidate: 60 } } as RequestInit & { next: unknown },
});
return <ul>{data.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}Type safe — no any
// Strict: noUncheckedIndexedAccess, exactOptionalPropertyTypes, verbatimModuleSyntax
type UserId = Brand<string, 'UserId'>; // branded — can't mix with PostId
import type { ApiResult, LoadState } from 'bbos-httpreq';
// Discriminated union — missing branch is a compile error
type State = LoadState<User>;
function View({ state }: { state: State }) {
switch (state.status) {
case 'success': return <div>{state.data.name}</div>;
case 'error': return <div>{state.error}</div>;
default: return null;
}
}
// Runtime check at the border with zod
import { z } from 'zod';
const UserSchema = z.object({ id: z.string(), name: z.string() });
const { data: raw } = await httpReq.get<unknown>('/users/1');
const user = UserSchema.parse(raw); // throws if server sent wrong shapeSee docs/app/guides/type-safe/page.tsx for the full guide.
Documentation
Full docs are in docs/ (Next.js 16, light theme, shiki github-light):
cd docs
npm install
npm run dev # http://localhost:3000
npm run build| Page | What you learn |
|---|---|
| Getting Started | Install, first request, instance |
| Core | create, mergeConfig, URL, HttpReqHeaders |
| Guides | Requests, Params, Headers, Interceptors, Errors, Cancel, File Upload, Retry/Dedupe/Cache, Type Safe |
| Examples | React / Next.js / Node & Edge |
| API | All options, HttpReqConfig, HttpReqResponse |
| Migration | From old http client — one import change |
Also see USAGE.md (quick reference) and RESEARCH.md (feature audit).
API at a glance
httpReq.get<T>(url, config?)
httpReq.post<T, D>(url, data?, config?)
httpReq.create(config) -> httpReq instance
httpReq.getUri(config) -> string
// Config highlights
{
params, paramsSerializer, headers, timeout, auth,
responseType, validateStatus, transformRequest, adapter,
signal, retry, dedupe, cache, redact, fetchOptions
}Development
npm run build # ESM dist/index.js + CJS dist/index.cjs
npm test # 168 tests across 11 files
npx tsc --noEmitLicense
MIT — see LICENSE.
