Skip to content
Zap Studio
fetch
Esc
navigateopen⌘Jpreview
On this page

createFetch

Create configured fetch clients with a shared base URL, headers, query params, and error defaults.

createFetch(...) creates a fetch client with shared baseURL, headers, query params, and error defaults. It returns an independent $fetch function and api method set bound to those defaults.

Basic Usage

import { createFetch } from "@zap-studio/fetch";
import { z } from "zod";

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

const { api, $fetch } = createFetch({
  baseURL: "https://api.example.com",
  headers: {
    Authorization: `Bearer ${process.env.API_TOKEN}`,
  },
});

const user = await api.get("/users/1", UserSchema);
const health = await $fetch("/health");

The returned $fetch and api have the same overloads, options, and error behavior as the top-level exports. Each createFetch instance is independent.

Options

All options are optional; unset options fall back to the global defaults.

Option Type Default Description
baseURL string "" Base URL that relative request URLs are resolved against.
headers HeadersInit undefined Default headers merged into every request. Per-request headers win.
searchParams URLSearchParams init undefined Default query params applied to every request.
throwOnFetchError boolean true Default for throwing FetchError on non-2xx responses.
throwOnValidationError boolean true Default for throwing ValidationError on validation issues.

Base URL

Relative URLs are resolved against baseURL using standard URL resolution. Absolute URLs keep their original origin.

import { createFetch } from "@zap-studio/fetch";
import { z } from "zod";

const UserSchema = z.object({ id: z.number(), name: z.string() });
const StatusSchema = z.object({ ok: z.boolean() });

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

await api.get("/users/1", UserSchema);
await api.get("https://status.example.com/health", StatusSchema);

Headers

Default headers are merged with per-request headers. Per-request headers win when the same header is set in both places.

import { createFetch } from "@zap-studio/fetch";
import { z } from "zod";

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

const defaultToken = process.env.API_TOKEN;
const overrideToken = process.env.ADMIN_TOKEN;

const { api } = createFetch({
  baseURL: "https://api.example.com",
  headers: {
    Authorization: `Bearer ${defaultToken}`,
  },
});

await api.get("/users/1", UserSchema, {
  headers: {
    Authorization: `Bearer ${overrideToken}`,
  },
});

Search Params

Query params merge in this order, with later values winning for duplicate keys:

  1. createFetch({ searchParams }) defaults
  2. query params already present in the request URL
  3. per-request searchParams
import { createFetch } from "@zap-studio/fetch";
import { z } from "zod";

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

const { api } = createFetch({
  baseURL: "https://api.example.com",
  searchParams: {
    locale: "en",
    page: "1",
  },
});

await api.get("/users?page=2", UserListSchema, {
  searchParams: {
    q: "ada",
  },
});
// Final URL: https://api.example.com/users?locale=en&page=2&q=ada

Default Throw Behavior

Set throw behavior once for a client and override it per request when needed.

import { createFetch } from "@zap-studio/fetch";

const { $fetch } = createFetch({
  throwOnFetchError: false,
});

const response = await $fetch("https://api.example.com/users/missing");

console.log(response.status); // e.g. 404, no FetchError thrown

Last updated on September 15, 2026

Was this page helpful?