Core Features

Core Features

CallApi offers a range of powerful features that simplify API calls and error handling, making it easier to work with APIs in your projects. Here's a detailed look at some of the key features CallApi provides:

✔️ Easy Error Handling

CallApi simplifies error management by returning an error object that captures both HTTP errors (errors from the server's response) and JavaScript errors (e.g., TypeError, SyntaxError). This unified structure helps you handle errors in a consistent way.

The error object contains the following properties:

  1. name: The type of error (e.g., 'HTTPError', 'TypeError', 'SyntsaxError').
  2. message: A brief description of what went wrong.
  3. errorData: The detailed error information, which can either be:
    • The parsed response from the API if it's an HTTP error.
    • The original JavaScript error object for non-HTTP errors.

For HTTP errors, the name will be 'HTTPError', and errorData will contain the API's error response (which could be an object or a string, depending on the server).

const { data, error } = await callApi("some-url");
 
console.log(error.name); // 'HTTPError'
console.log(error.message); // '404 Not Found'
console.log(error.errorData); // { error: "Resource not found" }

For non-HTTP errors (like TypeError or SyntaxError), name will reflect the JavaScript error type, and errorData will reference the corresponding JavaScript Error object.

const { data, error } = await callApi("some-url");
 
console.log(error.name); // 'TypeError'
console.log(error.message); // 'Failed to fetch'
console.log(error.errorData); // The original TypeError object

This structure makes it easy to identify and respond to different types of errors during API calls.

✔️ Automatic Request Cancellation

CallApi automatically prevents race conditions by managing API requests for you.

Whenever you make multiple requests to the same URL with the same options, CallApi ensures only the most recent request is processed. This avoids issues like redundant requests or conflicting data responses when multiple calls are made quickly in succession.

How It Works

  1. When a request is initiated, CallApi checks if another request to the same URL with the same options is still in progress.
  2. If a previous request exists, it's automatically cancelled.
  3. The latest request is then sent, ensuring only it gets processed.
  4. This cycle continues, ensuring no race conditions occur.

This feature is useful in scenarios like search inputs or form submissions where multiple requests could be triggered by user interactions.

Automatic Cancellation of Redundant Requests

Why This is Useful

  • Automatic Cancellation: If multiple requests to the same URL happen in quick succession, CallApi cancels any previous requests, ensuring only the latest one is processed.
  • Eliminates Race Conditions: This is especially useful when rapid API calls occur, such as during user input in search fields or multiple button clicks.
  • Seamless with React Hooks: This feature is perfect for React hooks like useEffect, where component updates might trigger frequent API requests.
  • Configurable: If you want to manage your requests differently, simply disable this feature by setting { cancelRedundantRequests: false } in your options.
  • Manual Control: For even more control, you can manually cancel requests using AbortController, just like with the native Fetch API.

Manually Cancelling Requests with AbortController

You can cancel a request manually using the AbortController. Here's an example:

const controller = new AbortController();
 
callApi("some-url", { signal: controller.signal });
 
// Later, if you need to cancel the request
controller.abort();

✔️ Authorization Headers

CallApi makes it simple to generate Authorization headers by using an auth property. If you pass in a string, typically used for tokens, CallApi will automatically generate a Bearer Authorization header. When you pass an object, you have the flexibility to choose between two options: using the bearer property to generate a Bearer Auth header, or using the token property to generate a Token Auth header.

For example, if you pass a string:

callApi("some-url", { auth: "token12345" });

This is equivalent to writing the following with Fetch:

fetch("some-url", {
	headers: { Authorization: `Bearer token12345` },
});

Alternatively, if you pass an object, you can specify the type of authorization. To generate a Bearer Auth header, use:

callApi("some-url", { auth: { bearer: "token12345" } });

Or, for Token Auth, use:

callApi("some-url", { auth: { token: "token12345" } });

These are equivalent to the following Fetch requests:

// For Bearer Auth
fetch("some-url", {
	headers: { Authorization: `Bearer token12345` },
});
 
// For Token Auth
fetch("some-url", {
	headers: { Authorization: `Token token12345` },
});

By simplifying the process of handling authorization headers, CallApi ensures that your requests are authenticated with minimal effort.

✔️ Handling the Response Format

CallApi supports all response types offered by the fetch API, such as json, text, blob, formData, etc. This means you don't need to handle response.json() manually, response.text(), response.formData(), etc.

The responseType option allows you to configure your preferred response type. The default type is json.

// Json (default)
const { data } = await callApi("url", { responseType: "json" });
// Text
const { data } = await callApi("url", { responseType: "text" });
// Blob, etc
const { data } = await callApi("url", { responseType: "blob" });
 
// Doing this in Fetch would imply:
const response = await fetch("some-url");
 
const data = await response.json(); // Or response.text() or response.blob() etc

This simplifies fetching data from responses, allowing you to specify the format you want directly.

Custom response parser

Suppose you want to parse a response with a custom function other than the default JSON.parse. In that case, you can pass a custom parser function to the responseParser option.

const { data, error } = await callApi("url", {
	responseParser: customResponseParser,
});

Or even better, provide it as a base option for callApi.

const callAnotherApi = callApi.create({
	responseParser: customResponseParser,
});

Custom body serializer

You could also provide a custom serializer/stringifier, other than the default JSON.stringify, for objects passed to the request body via the bodySerializer option.

const callAnotherApi = callApi.create({
	bodySerializer: customBodySerializer,
});

These options give you flexibility in handling both responses and request bodies in the way that best suits your needs.

✔️ Content-Type Generation

CallApi takes care of setting the Content-Type for you based on the body content. Here's how it works:

  • Automatic Content-Type Setting: CallApi automatically sets the Content-Type depending on your body data. Data types eligible for this automatic setting include:

  • Object

  • Query Strings

  • FormData

  • Object/JSON Handling: If you pass in an object as the request body, CallApi will set Content-Type to application/json for you. It will also handle JSON.stringify process for you (although you can always pass in a custom stringifier/serializer if you want), so you don't have to do it yourself.

callApi.post("some-url", {
	body: { message: "Good game" },
});
 
// The above request is equivalent to this
fetch("some-url", {
	method: "post",
	headers: { "Content-Type": "application/json" },
	body: JSON.stringify({ message: "Good game" }),
});
  • Query String Handling: If you pass in a query string as the request body, CallApi will ensure the Content-Type is set to application/x-www-form-urlencoded for you.

    CallApi also provides a toQueryString utility that helps you convert objects to query strings, for extra convenience.

import { toQueryString } from "@zayne-labs/callapi/utils";
 
callApi("some-url", {
	method: "POST",
	body: toQueryString({ message: "Good game" }),
});
 
// The above request is equivalent to this
fetch("some-url", {
	method: "post",
	headers: { "Content-Type": "application/x-www-form-urlencoded" },
	body: "message=Good%20game",
});
  • FormData Handling: If you pass in a FormData object as the request body, callApi will let the native fetch function handle the Content-Type. Generally, this will be multipart/form-data with the default boundary.
const data = new FormData(formElement);
 
callApi("some-url", { body: data });

✔️ Validator Function

CallApi also provides a responseValidator option, allowing you to pass in a function that validates the data returned from the server.

A good use case for this would be to use a library like Zod and pass its schema parse or safeParse function to validate the data.

If your validator function throws an error and the throwOnError option is set to true, you must check and handle the errors in a catch block. However, if throwOnError is set to false (the default), the error object will be returned as usual.

const callMainApi = callApi.create({
	responseValidator: zodSchema.parse, // or zodSchema.safeParse or any other validator you wish to use
});

This feature helps ensure the data you receive meets your validation criteria, making error handling more robust and predictable.

✔️ Query search params

You can add a query object as an option, and callApi will automatically create a query string for you.

callApi("some-url", {
	query: {
		param1: "value1",
		param2: "to encode",
	},
});
 
// The above request can be written in Fetch like this:
fetch("some-url?param1=value1&param2=to%20encode");