<h1 align="center">CallApi</h1>

<p align="center">
  <img src="https://raw.githubusercontent.com/zayne-labs/callapi/refs/heads/main/apps/docs/public/logo.png" alt="CallApi Logo" width="30%">
</p>

<p align="center">
   <!-- <a href="https://deno.bundlejs.com/badge?q=@zayne-labs/callapi,@zayne-labs/callapi&treeshake=%5B*%5D,%5B%7B+createFetchClient+%7D%5D&config=%7B%22compression%22:%7B%22type%22:%22brotli%22,%22quality%22:11%7D%7D"><img src="https://deno.bundlejs.com/badge?q=@zayne-labs/callapi,@zayne-labs/callapi&treeshake=%5B*%5D,%5B%7B+createFetchClient+%7D%5D&config=%7B%22compression%22:%7B%22type%22:%22brotli%22,%22quality%22:11%7D%7D" alt="bundle size"></a> -->
   <a href="https://www.npmjs.com/package/@zayne-labs/callapi"><img src="https://img.shields.io/npm/v/@zayne-labs/callapi?style=flat&color=EFBA5F" alt="npm version"></a>
   <a href="https://github.com/zayne-labs/callapi/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/@zayne-labs/callapi?style=flat&color=EFBA5F" alt="license"></a>
   <a href="https://www.npmjs.com/package/@zayne-labs/callapi"><img src="https://img.shields.io/npm/dm/@zayne-labs/callapi?style=flat&color=EFBA5F" alt="downloads per month"></a>
   <a href="https://github.com/zayne-labs/callapi/graphs/commit-activity"><img src="https://img.shields.io/github/commit-activity/m/zayne-labs/callapi?style=flat&color=EFBA5F" alt="commit activity"></a>
   <a href="https://deepwiki.com/zayne-labs/callapi"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
   <a href="https://code2tutorial.com/tutorial/f77cfbd0-3c37-4c37-9608-b3c977e46f00/index.md"><img src="https://img.shields.io/badge/Code2Tutorial-blue?color=blue&logo=victoriametrics" alt="Code2Tutorial"></a>
 </p>

<p align="center">
  <b>An advanced fetch library that actually solves real problems.</b>
</p>

<p align="center">
  <a href="https://zayne-labs-callapi.vercel.app"><b>Documentation</b></a> ·
  <a href="https://zayne-labs-callapi.vercel.app/docs/getting-started"><b>Getting Started</b></a> ·
  <a href="https://github.com/zayne-labs/callapi/tree/main/packages/callapi-plugins"><b>Plugins</b></a>
</p>

---

## Why CallApi?

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.**

```js
import { callApi } from "@zayne-labs/callapi";

const { data, error } = await callApi("/api/users");
```

## Features

### Request Deduplication

User spam-clicks a button? Handled. No race conditions.

```js
const req1 = callApi("/api/user");
const req2 = callApi("/api/user"); // Cancels req1 (can be configured to share it's response instead)
```

### Smart Response Parsing

Looks at Content-Type and parses accordingly.

```js
const { data } = await callApi("/api/data"); // JSON? Parsed.
```

### Error Handling

Structured errors make robust error handling trivial.

```js
const { data, error } = await callApi("/api/users");

if (error) {
	console.log(error.name); // "HTTPError", "ValidationError"
	console.log(error.errorData); // Actual API response
}
```

### Retries

Supports exponential backoff and custom retry conditions.

```js
await callApi("/api/data", {
	retryAttempts: 3,
	retryStrategy: "exponential",
	retryStatusCodes: [429, 500, 502, 503],
});
```

### Schema Validation

TypeScript-first types with runtime validation.

```ts
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 },
});
```

### Hooks

Hook into CallApi's lifecycle at any point.

```js
const api = createFetchClient({
	onRequest: ({ request }) => {
		request.headers.Authorization = `Bearer ${token}`;
	},
	onError: ({ error }) => {
		Sentry.captureException(error);
	},
	onResponseStream: ({ event }) => {
		console.log(`Downloaded ${event.progress}%`);
	},
});
```

### Plugins

Extend functionality with setup, hooks, and middleware.

```js
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],
});
```

### URL Helpers

Dynamic params, query strings, and method prefixes.

```js
await callApi("/users/:id", { params: { id: 123 } });
await callApi("/search", { query: { q: "test" } });
await callApi("@delete/users/123");
```

And so much more

See the [full documentation](https://zayne-labs-callapi.vercel.app/docs) for the full list of features.

## Installation

```bash
npm install @zayne-labs/callapi
```

```js
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),
});
```

### CDN

```html
<script type="module">
	import { callApi } from "https://esm.run/@zayne-labs/callapi@latest";
</script>
```

## What makes it worth considering?

- **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

## License

MIT © [Ryan Zayne](https://github.com/ryan-zayne)
