Request & Response Helpers

Content type handling, response conversion, and request body processing

Automatic Content-Type Detection

The Content-Type header is automatically set based on the request body.

Data types eligible for this automatic setting include:

  • Object literals → application/json
  • Query Strings → application/x-www-form-urlencoded
  • FormData → multipart/form-data (handled by the browser)
api.ts
import { callApi } from "@zayne-labs/callapi";

// application/json
await callApi("https://api.example.com/data", {
	method: "POST",
	body: { name: "John", age: 30 },
});

// application/x-www-form-urlencoded
await callApi("https://api.example.com/form", {
	method: "POST",
	body: "name=John&age=30",
});

// The browser will handle the Content-Type automatically (multipart/form-data)
await callApi("https://api.example.com/upload", {
	method: "POST",
	body: formData,
});

Override the default content type if the need arises:

api.ts
import { callApi } from "@zayne-labs/callapi";

await callApi("https://api.example.com/custom", {
	method: "POST",
	body: data,
	headers: {
		"Content-Type": "application/custom+json",
	},
});

Automatic Response Parser

Responses are automatically parsed based on the response type specified in the responseType option.

Available response types include:

api.ts
import { callApi } from "@zayne-labs/callapi";

// `json` response (default)
const { data } = await callApi<{ name: string; age: number }>("https://api.example.com/user");

// Other response types
const { data: imageBlob } = await callApi("https://api.example.com/image", {
	responseType: "blob",
});

const { data: textContent } = await callApi("https://api.example.com/text", {
	responseType: "text",
});

// Doing any of the above in Fetch would imply the following extra steps:
const response = await fetch("some-url");

const data = await response.json(); // Or response.text() or response.blob() etc

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:

api.ts
import { callApi } from "@zayne-labs/callapi";

const { data, error } = await callApi("https://api.example.com/data", {
	responseParser: (responseString) => customParser(responseString),
});

Custom Request Bodies

CallApi naturally supports all request body types that Fetch does, with a few additional conveniences:

Object literals

api.ts
import { callApi } from "@zayne-labs/callapi";

const result = await callApi("https://api.example.com/user", {
	method: "POST",
	body: { name: "John", age: 30 },
});

// Doing this in Fetch would imply:
const response = await fetch("https://api.example.com/user", {
	method: "POST",
	headers: { "Content-Type": "application/json" },
	body: JSON.stringify({ name: "John", age: 30 }),
});

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.

api.ts
import { callApi } from "@zayne-labs/callapi";
const result = await callApi("https://api.example.com/data", {
	method: "POST",
	body: { name: "John", age: 30 },
	bodySerializer: (body) => serialize(body),
});

Query Strings

Fetch supports query strings as a request body out of the box. However this usually requires manual setting with new URLSearchParams() and set() or append().

CallApi provides the toQueryString utility that converts objects to URL-encoded strings:

api.ts
import { callApi } from "@zayne-labs/callapi";
import { toQueryString } from "@zayne-labs/callapi/utils";

const result = await callApi("https://api.example.com/search", {
	method: "POST",
	body: toQueryString({ name: "John", age: 30 }),
});

// The above request is equivalent to this
fetch("https://api.example.com/search", {
	method: "POST",
	headers: { "Content-Type": "application/x-www-form-urlencoded" },
	body: "name=John&age=30",
});