Skip to main content
The defineSchema utility provides type-safe schema definition for CallApi clients. It helps you create strongly-typed API schemas with routes and configuration that enable end-to-end type safety across your application.

Import

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

Signature

function defineSchema<
  TBaseSchemaRoutes extends BaseCallApiSchemaRoutes,
  TSchemaConfig extends CallApiSchemaConfig
>(
  routes: TBaseSchemaRoutes,
  config?: TSchemaConfig
): BaseCallApiSchemaAndConfig

Parameters

Returns

Returns a BaseCallApiSchemaAndConfig object containing:
  • routes: The defined routes with deep writeable types
  • config: The schema configuration with deep writeable types

Usage

Basic Schema Definition

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

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

const apiSchema = defineSchema({
  "/users/:id": {
    response: userSchema,
  },
  "/users": {
    response: z.array(userSchema),
  },
});

const api = createFetchClient({
  baseURL: "https://api.example.com",
  schema: apiSchema,
});

// Fully typed response
const { data } = await api("/users/123");
// data is typed as { id: number; name: string; email: string }

Schema with Configuration

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

const apiSchema = defineSchema(
  {
    "/users/:id": {
      response: z.object({
        id: z.number(),
        name: z.string(),
      }),
    },
  },
  {
    strict: true, // Enable strict mode for all routes
  }
);

Complete API Schema

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

const createUserSchema = z.object({
  name: z.string(),
  email: z.string().email(),
});

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

const apiSchema = defineSchema({
  "POST /users": {
    body: createUserSchema,
    response: userResponseSchema,
  },
  "GET /users/:id": {
    response: userResponseSchema,
  },
  "GET /users": {
    response: z.array(userResponseSchema),
  },
});

const api = createFetchClient({
  baseURL: "https://api.example.com",
  schema: apiSchema,
});

// Type-safe request with body validation
const { data } = await api("POST /users", {
  body: {
    name: "John Doe",
    email: "[email protected]",
  },
});
// data is fully typed based on userResponseSchema

defineSchemaRoutes

Defines only the routes portion of a schema:
import { defineSchemaRoutes } from "@zayne-labs/callapi/utils";
import { z } from "zod";

const routes = defineSchemaRoutes({
  "/users/:id": {
    response: z.object({ id: z.number(), name: z.string() }),
  },
});

defineSchemaConfig

Defines only the schema configuration:
import { defineSchemaConfig } from "@zayne-labs/callapi/utils";

const config = defineSchemaConfig({
  strict: true,
});

defineMainSchema

Defines a complete schema with both routes and config already combined:
import { defineMainSchema } from "@zayne-labs/callapi/utils";
import { z } from "zod";

const mainSchema = defineMainSchema({
  routes: {
    "/users/:id": {
      response: z.object({ id: z.number() }),
    },
  },
  config: {
    strict: true,
  },
});

defineBaseConfig

Helper for defining base configuration with type safety.

Import

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

Usage

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

const baseConfig = defineBaseConfig({
  baseURL: "https://api.example.com",
  retryAttempts: 3,
  timeout: 5000,
  onError: ({ error }) => {
    console.error("API Error:", error);
  },
});

const api = createFetchClient(baseConfig);
You can also use it with a function for dynamic configuration:
const baseConfig = defineBaseConfig((context) => ({
  baseURL: "https://api.example.com",
  headers: {
    "X-Request-ID": context.requestId,
  },
}));

defineInstanceConfig

Helper for defining instance-level configuration with type safety.

Import

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

Usage

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

const instanceConfig = defineInstanceConfig({
  retryAttempts: 5,
  dedupeStrategy: "defer",
  onSuccess: ({ data }) => {
    console.log("Request successful:", data);
  },
});

// Use when making requests
const { data } = await api("/users", instanceConfig);

Type Safety

The defineSchema utility provides deep type safety:
  • Route keys are validated against your schema
  • Request bodies are type-checked against body schemas
  • Response data is automatically typed from response schemas
  • URL parameters are inferred from route patterns
const schema = defineSchema({
  "GET /users/:userId/posts/:postId": {
    response: z.object({ id: z.number(), title: z.string() }),
  },
});

const api = createFetchClient({ schema });

// TypeScript knows about userId and postId parameters
const { data } = await api("GET /users/123/posts/456");
// data is typed as { id: number; title: string }

See Also

Build docs developers (and LLMs) love