Migration Guide

Migrate to CallApi from other fetch libraries

This guide helps you migrate from other popular fetch libraries to CallApi with step-by-step instructions and code examples.

From Axios

Installation

First, install CallApi alongside or instead of Axios:

	pnpm install @zayne-labs/callapi # Optionally remove axios npm uninstall axios

Basic Request

import axios from "axios";

// GET request
const responseOne = await axios.get("https://api.example.com/users");
const users = responseOne.data;

// POST request
const responseTwo = await axios.post("https://api.example.com/users", {
	name: "John Doe",
	email: "john@example.com",
});
const user = responseTwo.data;
import { callApi } from "@zayne-labs/callapi";

// GET request
const { data: users } = await callApi("https://api.example.com/users");

// POST request
const { data: user } = await callApi("@post/https://api.example.com/users", {
	body: {
		name: "John Doe",
		email: "john@example.com",
	},
});

Creating an Instance

import axios from "axios";

const api = axios.create({
	baseURL: "https://api.example.com",
	timeout: 10000,
	headers: {
		"Content-Type": "application/json",
	},
});

const response = await api.get("/users");
import { createFetchClient } from "@zayne-labs/callapi";

const api = createFetchClient({
	baseURL: "https://api.example.com",
	timeout: 10000,
	headers: {
		"Content-Type": "application/json",
	},
});

const { data } = await api("/users");

Interceptors → Hooks

import axios from "axios";

const api = axios.create({
	baseURL: "https://api.example.com",
});

// Request interceptor
api.interceptors.request.use(
	(config) => {
		const token = localStorage.getItem("token");
		if (token) {
			config.headers.Authorization = `Bearer ${token}`;
		}
		return config;
	},
	(error) => Promise.reject(error)
);

// Response interceptor
api.interceptors.response.use(
	(response) => {
		console.log("Response:", response.status);
		return response;
	},
	(error) => {
		if (error.response?.status === 401) {
			// Redirect to login
		}
		return Promise.reject(error);
	}
);
import { createFetchClient } from "@zayne-labs/callapi";

const api = createFetchClient({
	baseURL: "https://api.example.com",

	// Request hook
	onRequest: ({ request }) => {
		const token = localStorage.getItem("token");
		if (token) {
			request.headers.set("Authorization", `Bearer ${token}`);
		}
	},

	// Success hook
	onSuccess: ({ response }) => {
		console.log("Response:", response.status);
	},

	// Error hook
	onError: ({ response }) => {
		if (response?.status === 401) {
			// Redirect to login
		}
	},
});

Error Handling

import axios from "axios";

try {
	const response = await axios.get("/users");
	const users = response.data;
} catch (error) {
	if (axios.isAxiosError(error)) {
		console.error("Status:", error.response?.status);
		console.error("Data:", error.response?.data);
		console.error("Message:", error.message);
	} else {
		console.error("Unexpected error:", error);
	}
}
import { callApi } from "@zayne-labs/callapi";
import { isHTTPError } from "@zayne-labs/callapi/utils";

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

if (error) {
	if (isHTTPError(error)) {
		console.error("Status:", error.response?.status);
		console.error("Data:", error.errorData);
		console.error("Message:", error.message);
	} else {
		console.error("Unexpected error:", error.message);
	}
}

Request Cancellation

import axios from "axios";

const controller = new AbortController();

try {
	const response = await axios.get("/users", {
		signal: controller.signal,
	});
} catch (error) {
	if (axios.isCancel(error)) {
		console.log("Request cancelled");
	}
}

// Cancel the request
controller.abort();
import { callApi } from "@zayne-labs/callapi";

const controller = new AbortController();

const { data, error } = await callApi("/users", {
	signal: controller.signal,
});

if (error?.name === "AbortError") {
	console.log("Request cancelled");
}

// Cancel the request
controller.abort();

TypeScript Support

import axios from "axios";

type User = {
	email: string;
	id: number;
	name: string;
};

// Manual typing
const response = await axios.get<User>("/users/1");
const user = response.data; // Type: User

// No runtime validation
import { callApi } from "@zayne-labs/callapi";
import { z } from "zod";

const userSchema = z.object({
	id: z.number(),
	name: z.string(),
	email: z.string(),
});

// Automatic type inference + runtime validation
const { data: user } = await callApi("/users/1", {
	schema: { data: userSchema },
});
// Type automatically inferred from schema!

From Ky

Installation

bash npm install @zayne-labs/callapi npm uninstall ky
bash pnpm add @zayne-labs/callapi pnpm remove ky
bash yarn add @zayne-labs/callapi yarn remove ky
bash bun add @zayne-labs/callapi bun remove ky

Basic Usage

import ky from "ky";

// GET request
const users = await ky.get("https://api.example.com/users").json();

// POST request
const user = await ky
	.post("https://api.example.com/users", {
		json: {
			name: "John Doe",
			email: "john@example.com",
		},
	})
	.json();
import { callApi } from "@zayne-labs/callapi";

// GET request
const { data: users } = await callApi("https://api.example.com/users");

// POST request
const { data: user } = await callApi("@post/https://api.example.com/users", {
	body: {
		name: "John Doe",
		email: "john@example.com",
	},
});

Creating an Instance

import ky from "ky";

const api = ky.create({
	prefixUrl: "https://api.example.com",
	timeout: 10000,
	retry: 2,
});

const users = await api.get("users").json();
import { createFetchClient } from "@zayne-labs/callapi";

const api = createFetchClient({
	baseURL: "https://api.example.com",
	timeout: 10000,
	retryAttempts: 2,
});

const { data: users } = await api("/users");

Hooks

import ky from "ky";

const api = ky.create({
	hooks: {
		beforeRequest: [
			(request) => {
				request.headers.set("Authorization", `Bearer ${token}`);
			},
		],
		afterResponse: [
			(request, options, response) => {
				console.log("Response:", response.status);
			},
		],
	},
});
import { createFetchClient } from "@zayne-labs/callapi";

const api = createFetchClient({
	onRequest: ({ request }) => {
		request.headers.set("Authorization", `Bearer ${token}`);
	},
	onSuccess: ({ response }) => {
		console.log("Response:", response.status);
	},
});

Error Handling

import ky from "ky";

try {
	const users = await ky.get("api/users").json();
} catch (error) {
	if (error instanceof ky.HTTPError) {
		console.error("Status:", error.response.status);
		const errorData = await error.response.json();
		console.error("Error data:", errorData);
	}
}
import { callApi } from "@zayne-labs/callapi";
import { isHTTPError } from "@zayne-labs/callapi/utils";

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

if (isHTTPError(error)) {
	console.error("Status:", error.response?.status);
	console.error("Error data:", error.errorData); // Already parsed!
}

From Ofetch

Installation

bash npm install @zayne-labs/callapi npm uninstall ofetch
bash pnpm add @zayne-labs/callapi pnpm remove ofetch
bash yarn add @zayne-labs/callapi yarn remove ofetch
bash bun add @zayne-labs/callapi bun remove ofetch

Basic Usage

import { ofetch } from "ofetch";

// GET request
const users = await ofetch("https://api.example.com/users");

// POST request
const user = await ofetch("https://api.example.com/users", {
	method: "POST",
	body: {
		name: "John Doe",
		email: "john@example.com",
	},
});
import { callApi } from "@zayne-labs/callapi";

// GET request
const { data: users } = await callApi("https://api.example.com/users");

// POST request
const { data: user } = await callApi("@post/https://api.example.com/users", {
	body: {
		name: "John Doe",
		email: "john@example.com",
	},
});

Creating an Instance

import { ofetch } from "ofetch";

const api = ofetch.create({
	baseURL: "https://api.example.com",
	retry: 2,
	retryDelay: 1000,
});

const users = await api("/users");
import { createFetchClient } from "@zayne-labs/callapi";

const api = createFetchClient({
	baseURL: "https://api.example.com",
	retryAttempts: 2,
	retryDelay: 1000,
});

const { data: users } = await api("/users");

Interceptors → Hooks

import { ofetch } from "ofetch";

const api = ofetch.create({
	baseURL: "https://api.example.com",

	onRequest: ({ options }) => {
		options.headers = {
			...options.headers,
			Authorization: `Bearer ${token}`,
		};
	},

	onResponse: ({ response }) => {
		console.log("Response:", response.status);
	},

	onResponseError: ({ response }) => {
		console.error("Error:", response.status);
	},
});
import { createFetchClient } from "@zayne-labs/callapi";

const api = createFetchClient({
	baseURL: "https://api.example.com",

	onRequest: ({ request }) => {
		request.headers.set("Authorization", `Bearer ${token}`);
	},

	onSuccess: ({ response }) => {
		console.log("Response:", response.status);
	},

	onError: ({ response }) => {
		console.error("Error:", response?.status);
	},
});

Error Handling

import { ofetch } from "ofetch";

try {
	const users = await ofetch("/users");
} catch (error) {
	console.error("Error:", error.data);
	console.error("Status:", error.statusCode);
}
import { callApi } from "@zayne-labs/callapi";
import { isHTTPError } from "@zayne-labs/callapi/utils";

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

if (isHTTPError(error)) {
	console.error("Error:", error.errorData);
	console.error("Status:", error.response?.status);
}

Common Patterns

Authentication

// Various approaches depending on library
// Usually involves interceptors or hooks
import { createFetchClient } from "@zayne-labs/callapi";

const api = createFetchClient({
	baseURL: "https://api.example.com",

	onRequest: ({ request, options }) => {
		const token = localStorage.getItem("token");

		if (token) {
			options.auth = token;
		}
	},

	onError: async ({ error, response, options }) => {
		// Refresh token on 401
		if (response?.status === 401) {
			const newToken = await refreshToken();

			localStorage.setItem("token", newToken);

			// Retry request
			options.retryAttempts = 1;
		}
	},
});

File Upload with Progress

import axios from "axios";

const formData = new FormData();
formData.append("file", file);

const response = await axios.post("/upload", formData, {
	onUploadProgress: (progressEvent) => {
		const progress = (progressEvent.loaded / progressEvent.total) * 100;
		console.log(`Upload progress: ${progress}%`);
	},
});
import { callApi } from "@zayne-labs/callapi";

const formData = new FormData();
formData.append("file", file);

const { data } = await callApi("@post/upload", {
	body: formData,
	onResponseStream: ({ event }) => {
		if (event.type === "progress") {
			console.log(`Upload progress: ${event.progress}%`);
		}
	},
});

Query Parameters

// Manual URL construction
const responseOne = await api.get("/users?role=admin&status=active");

// Or using params option (varies by library)
const responseTwo = await api.get("/users", {
	params: { role: "admin", status: "active" },
});
import { callApi } from "@zayne-labs/callapi";

// Built-in query helper
const { data: users } = await callApi("/users", {
	query: { role: "admin", status: "active" },
});
// Automatically constructs: /users?role=admin&status=active

URL Parameters

// Manual URL construction
const userId = 123;
const response = await api.get(`/users/${userId}`);
import { callApi } from "@zayne-labs/callapi";

// Built-in param substitution
const { data: user } = await callApi("/users/:id", {
	params: { id: 123 },
});
// Automatically constructs: /users/123

Migration Checklist

  • Install @zayne-labs/callapi
  • Update imports from axios/ky/ofetch to callApi
  • Replace axios.create() or ky.create() with createFetchClient()
  • Update HTTP method calls (api.get(), api.post()) to either callApi('@method/path') or use the method option.
  • Convert interceptors to hooks (onRequest, onSuccess, onError)
  • Update error handling to check the error property in the returned result object and use isHTTPError(error)
  • For React Query, see the React Query Integration guide.
Edit on GitHub

Last updated on

On this page