npm version license downloads per month commit activity Ask DeepWiki Code2Tutorial
An advanced fetch library that actually solves real problems.
Documentation · Getting Started · Plugins
Fetch is too basic for real apps. You end up writing the same boilerplate: error handling, retries, deduplication, response parsing etc. CallApi handles all of that and practically more.
Drop-in replacement for fetch. Under 6KB. All kinds of convenience features. Zero dependencies.
import { callApi } from "@zayne-labs/callapi"; const { data, error } = await callApi("/api/users");
User spam-clicks a button? Handled. No race conditions.
const req1 = callApi("/api/user"); const req2 = callApi("/api/user"); // Cancels req1 (can be configured to share it's response instead)
Looks at Content-Type and parses accordingly.
const { data } = await callApi("/api/data"); // JSON? Parsed.
Structured errors make robust error handling trivial.
const { data, error } = await callApi("/api/users"); if (error) { console.log(error.name); // "HTTPError", "ValidationError" console.log(error.errorData); // Actual API response }
Supports exponential backoff and custom retry conditions.
await callApi("/api/data", { retryAttempts: 3, retryStrategy: "exponential", retryStatusCodes: [429, 500, 502, 503], });
TypeScript-first types with runtime validation.
import { createFetchClient } from "@zayne-labs/callapi"; import { defineSchema } from "@zayne-labs/callapi/utils"; import { z } from "zod"; const callMainApi = createFetchClient({ schema: defineSchema({ "/users/:id": { data: z.object({ id: z.number(), name: z.string(), }), }, }), }); // Fully typed + validated const user = await callMainApi("/users/:id", { params: { id: 123 }, });
Hook into CallApi's lifecycle at any point.
const api = createFetchClient({ onRequest: ({ request }) => { request.headers.set("Authorization", `Bearer ${token}`); }, onError: ({ error }) => { Sentry.captureException(error); }, onResponseStream: ({ event }) => { console.log(`Downloaded ${event.progress}%`); }, });
Extend functionality with setup, hooks, and middleware.
const metricsPlugin = definePlugin({ id: "metrics", name: "Metrics Plugin", setup: ({ options }) => ({ options: { ...options, meta: { startTime: Date.now() }, }, }), hooks: { onSuccess: ({ options }) => { const duration = Date.now() - options.meta.startTime; console.info(`Request took ${duration}ms`); }, }, middlewares: { fetchMiddleware: (ctx) => async (input, init) => { console.info("→", input); const response = await ctx.fetchImpl(input, init); console.info("←", response.status); return response; }, }, }); const api = createFetchClient({ plugins: [metricsPlugin], });
Dynamic params, query strings, and method prefixes.
await callApi("/users/:id", { params: { id: 123 } }); await callApi("/search", { query: { q: "test" } }); await callApi("@delete/users/123");
And so many more
See the full documentation for the full list of features.
npm install @zayne-labs/callapi
import { callApi, createFetchClient } from "@zayne-labs/callapi"; // Simple const { data } = await callApi("/api/users"); // Configured const api = createFetchClient({ baseURL: "https://api.example.com", retryAttempts: 2, timeout: 10000, onError: ({ error }) => trackError(error), });
<script type="module"> import { callApi } from "https://esm.run/@zayne-labs/callapi"; </script>
- TypeScript-first - Full inference everywhere
- Familiar API - If you know fetch, you know CallApi
- Actually small - Zero dependencies and Under 6KB, unlike other 50kb libs in the wild
- Fast - Built on native Web APIs
- Works everywhere - Browsers, Node 18+, Deno, Bun, Cloudflare Workers
MIT © Ryan Zayne