@jerryshim/fetch-kit
v1.3.0
Published
fetch-based HTTP client with errors and middleware
Readme
@jerryshim/fetch-kit
Lightweight fetch-based HTTP client with middleware, typed errors, and flexible parsing.
Installation
pnpm add @jerryshim/fetch-kit
# or
npm i @jerryshim/fetch-kit
# or
yarn add @jerryshim/fetch-kitQuick Start
import { createHttpClient } from "@jerryshim/fetch-kit";
type SR<T> = { code: number; message: string; data: T };
type User = { id: string; name: string };
const http = createHttpClient({
baseUrl: "https://api.example.com",
before: [
(ctx) => {
const u = new URL(ctx.url);
u.searchParams.set("locale", "en");
ctx.url = u.toString();
ctx.init.headers = {
...(ctx.init.headers || {}),
Authorization: "Bearer TOKEN",
};
},
],
});
const user = await http.get<SR<User>, User>("/users/42", undefined, {
parse: "json",
select: (r) => (r as SR<User>).data,
});API
createHttpClient<E = unknown, Expand = unknown>(options)→ returns an object with:request(path, method, body?, init?)get(path, params?, init?)post(path, body?, init?)patch(path, body?, init?)put(path, body?, init?)del(path, params?, init?)
Params (querystring)
get/del accept params to build the querystring. Supported forms:
URLSearchParamsArray<[string, string]>string(e.g.,"a=1&b=2"or"?a=1&b=2")Record<string, string | number>— values are stringified;null/undefinedare skipped;string[]/number[]repeat keys.
Examples:
http.get("/items", new URLSearchParams({ a: "1", b: "x" }), { parse: "json" });
http.get(
"/items",
[
["a", "1"],
["b", "2"],
],
{ parse: "json" }
);
http.get("/items", "page=2&sort=asc", { parse: "text" });
http.get(
"/items",
{ page: 1, tags: ["a", "b"], q: "x", none: undefined },
{ parse: "json" }
);
// → /items?page=1&tags=a&tags=b&q=xOptions
baseUrl?: stringdefaults?: RequestInit & { timeout?: number } & ExpanddecodeError?: ({ response, context }) => Promise<{ message?: string; data?: E } | void>before?: Array<(ctx: RequestContext) => void | Promise<void>>after?: Array<(ctx: RequestContext, res: Response) => Response | void | Promise<Response | void>>onError?: (ctx: RequestContext, error: unknown) => Promise<Response | void>
Request options
timeout?: number(ms)parse?: 'json' | 'text' | 'blob' | 'arrayBuffer' | 'none'(default:'json')select?: (parsed) => anydecodeError?: same as global
Errors
FetchError— non-2xx HTTP responses. Includescode,data.TimeoutError—AbortErrormapped when timeout triggers.InternalServerError— unexpected runtime errors.
Middleware
before— mutatectx.url,ctx.init, andctx.bodybefore fetch.after— inspect/replaceResponsebefore parsing; chainable.onError— recover from thrown errors by returning aResponse.
TypeScript & Expand passthrough
You can pass extra fields through defaults/per-call init via the Expand generic, e.g., Next.js next/cache fields.
type NextExpand = {
next?: { revalidate?: number | false; tags?: string[] };
cache?: RequestCache;
};
const http = createHttpClient<unknown, NextExpand>({
defaults: { timeout: 5000, next: { revalidate: 60 } },
before: [
(ctx) => {
(ctx.init as any).next = { ...(ctx.init as any).next, tags: ["users"] };
},
],
});Notes
- Works in Node 18+ and browsers where
fetchis available. - Ships ESM and CJS builds; see
package.jsonexports.
