Skip to main content
CallApi provides helper functions to convert plain JavaScript objects into FormData or URLSearchParams. These utilities handle validation, type conversion, and proper formatting of complex data types.

toFormData

Converts a plain object to FormData, with optional schema validation.

Import

import { toFormData } from "@zayne-labs/callapi/utils";

Signature

function toFormData<TSchema extends CallApiSchemaType>(
  data: InferSchemaOutput<TSchema>,
  schema?: TSchema
): FormData

Parameters

Returns

Returns a FormData object with all entries from the input data.

Behavior

  • Primitives (string, number, boolean): Converted to strings
  • Blobs/Files: Added directly to FormData
  • Arrays: Each item is appended (allows multiple values for same key)
  • Objects: JSON stringified before adding to FormData
  • Null/Undefined: Skipped

Usage

Basic Usage

import { toFormData } from "@zayne-labs/callapi/utils";

const formData = toFormData({
  name: "John Doe",
  age: 30,
  active: true,
});

// FormData contains:
// name: "John Doe"
// age: "30"
// active: "true"

With File Uploads

import { toFormData } from "@zayne-labs/callapi/utils";

const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];

const formData = toFormData({
  avatar: file,
  username: "johndoe",
});

// Send as multipart/form-data
await api("/users/upload", {
  method: "POST",
  body: formData,
});

With Arrays

import { toFormData } from "@zayne-labs/callapi/utils";

const formData = toFormData({
  tags: ["javascript", "typescript", "nodejs"],
  name: "My Project",
});

// FormData contains:
// tags: "javascript"
// tags: "typescript" 
// tags: "nodejs"
// name: "My Project"

With Nested Objects

import { toFormData } from "@zayne-labs/callapi/utils";

const formData = toFormData({
  user: {
    name: "John",
    age: 30,
  },
  settings: {
    theme: "dark",
    notifications: true,
  },
});

// FormData contains:
// user: "{\"name\":\"John\",\"age\":30}"
// settings: "{\"theme\":\"dark\",\"notifications\":true}"

With Validation

import { toFormData } from "@zayne-labs/callapi/utils";
import { z } from "zod";

const userSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  age: z.number().positive(),
});

try {
  const formData = toFormData(
    {
      name: "John",
      email: "[email protected]",
      age: 30,
    },
    userSchema
  );
  
  // FormData is validated and ready to use
} catch (error) {
  if (error instanceof ValidationError) {
    console.error("Validation failed:", error.errorData);
  }
}

toSearchParams

Converts a plain object to URLSearchParams, with optional schema validation.

Import

import { toSearchParams } from "@zayne-labs/callapi/utils";

Signature

function toSearchParams<TSchema extends CallApiSchemaType>(
  data: InferSchemaOutput<TSchema>,
  schema?: TSchema
): URLSearchParams

Parameters

Returns

Returns a URLSearchParams object with all entries from the input data.

Behavior

  • Primitives: Converted to strings
  • Arrays: Each item is appended as a separate parameter
  • Objects: JSON stringified
  • Null/Undefined: Skipped

Usage

Basic Usage

import { toSearchParams } from "@zayne-labs/callapi/utils";

const params = toSearchParams({
  page: 1,
  limit: 10,
  sort: "name",
});

console.log(params.toString());
// "page=1&limit=10&sort=name"

With Arrays

import { toSearchParams } from "@zayne-labs/callapi/utils";

const params = toSearchParams({
  tags: ["javascript", "typescript"],
  category: "programming",
});

console.log(params.toString());
// "tags=javascript&tags=typescript&category=programming"

In API Calls

import { toSearchParams } from "@zayne-labs/callapi/utils";

const searchParams = toSearchParams({
  q: "react",
  page: 1,
  per_page: 20,
});

const { data } = await api("/search", {
  searchParams,
});

// Or use directly in URL
const { data: results } = await api(`/search?${searchParams.toString()}`);

With Validation

import { toSearchParams } from "@zayne-labs/callapi/utils";
import { z } from "zod";

const searchSchema = z.object({
  q: z.string().min(1),
  page: z.number().int().positive(),
  per_page: z.number().int().min(1).max(100),
});

try {
  const params = toSearchParams(
    {
      q: "typescript",
      page: 1,
      per_page: 50,
    },
    searchSchema
  );
  
  // Params are validated and ready to use
} catch (error) {
  if (error instanceof ValidationError) {
    console.error("Invalid search parameters:", error.errorData);
  }
}

toQueryString

Converts a plain object directly to a query string.

Import

import { toQueryString } from "@zayne-labs/callapi/utils";

Signature

function toQueryString<TSchema extends CallApiSchemaType>(
  data: InferSchemaOutput<TSchema>,
  schema?: TSchema
): string

Usage

import { toQueryString } from "@zayne-labs/callapi/utils";

const queryString = toQueryString({
  page: 1,
  limit: 10,
  tags: ["javascript", "typescript"],
});

console.log(queryString);
// "page=1&limit=10&tags=javascript&tags=typescript"

// Use directly in URL
const { data } = await api(`/search?${queryString}`);

Validation Errors

All helper functions throw a ValidationError if the schema validation fails:
import { toFormData, ValidationError } from "@zayne-labs/callapi/utils";
import { z } from "zod";

const schema = z.object({
  email: z.string().email(),
  age: z.number().positive(),
});

try {
  const formData = toFormData(
    {
      email: "invalid-email",
      age: -5,
    },
    schema
  );
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(error.name); // "ValidationError"
    console.log(error.issueCause); // "toFormData"
    console.log(error.errorData); // Array of validation issues
  }
}

Common Patterns

File Upload with Metadata

import { toFormData } from "@zayne-labs/callapi/utils";

const uploadFile = async (file: File, metadata: Record<string, unknown>) => {
  const formData = toFormData({
    file,
    ...metadata,
  });

  return api("/upload", {
    method: "POST",
    body: formData,
  });
};

await uploadFile(myFile, {
  description: "Profile picture",
  tags: ["profile", "avatar"],
});

Search with Filters

import { toSearchParams } from "@zayne-labs/callapi/utils";

const searchProducts = async (filters: {
  q?: string;
  category?: string;
  minPrice?: number;
  maxPrice?: number;
  tags?: string[];
}) => {
  const params = toSearchParams(filters);
  
  return api("/products", {
    searchParams: params,
  });
};

const { data } = await searchProducts({
  category: "electronics",
  minPrice: 100,
  maxPrice: 500,
  tags: ["laptop", "computer"],
});

Validated Form Submission

import { toFormData } from "@zayne-labs/callapi/utils";
import { z } from "zod";

const userFormSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  avatar: z.instanceof(File).optional(),
});

const submitUserForm = async (formData: z.infer<typeof userFormSchema>) => {
  const validated = toFormData(formData, userFormSchema);
  
  return api("/users", {
    method: "POST",
    body: validated,
  });
};

See Also

Build docs developers (and LLMs) love