Zap Studio

Using $fetch

Low-level fetch function with optional schema validation and raw Response access.

The $fetch function provides more control over your requests. You can use it with or without schema validation, making it flexible for different use cases.

When to Use $fetch

Use $fetch instead of api.* methods when you need:

  • Raw Response objects for headers, status codes, or streaming
  • Non-JSON responses (binary files, text, HTML)
  • Conditional validation based on response status
  • More control over the request/response cycle

With Schema Validation

When you pass a schema, $fetch validates the response and returns typed data:

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

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

const user = await $fetch("https://api.example.com/users/1", UserSchema, {
  headers: {
    Authorization: "Bearer token",
  },
});

// user is typed as { id: number; name: string; email: string }
console.log(user.name);

Without Schema Validation

When you don't pass a schema, $fetch returns the raw Response object:

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

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

// response is a standard Response object
console.log(response.status);       // 200
console.log(response.headers);      // Headers object
const data = await response.json(); // Manual parsing

Function Signatures

$fetch has two overloaded signatures:

// With schema - returns validated data
async function $fetch<TSchema extends StandardSchemaV1>(
  resource: string,
  schema: TSchema,
  options?: ExtendedRequestInit
): Promise<StandardSchemaV1.InferOutput<TSchema>>;

// Without schema - returns raw Response
async function $fetch(
  resource: string,
  options?: ExtendedRequestInit
): Promise<Response>;

Extended Request Options

OptionTypeDefaultDescription
bodyBodyInit | Record<string, unknown>-Request body (auto-stringified when schema is present)
searchParamsURLSearchParams | Record<string, string>-Query parameters
throwOnFetchErrorbooleantrueThrow FetchError on non-2xx responses
throwOnValidationErrorbooleantrueThrow ValidationError on schema validation failures

Plus all standard RequestInit options.

Examples

POST with JSON body

const user = await $fetch("https://api.example.com/users", UserSchema, {
  method: "POST",
  body: {
    name: "John Doe",
    email: "john@example.com",
  },
});

GET with query parameters

const url = new URL("https://api.example.com/users");
url.searchParams.set("page", "1");
url.searchParams.set("limit", "10");

const users = await $fetch(url.toString(), UsersSchema);

Handling non-JSON responses

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

if (response.ok) {
  const blob = await response.blob();
  // Handle the binary data
}

Custom headers

const user = await $fetch("https://api.example.com/users/1", UserSchema, {
  headers: {
    Authorization: "Bearer token",
    "Accept-Language": "en-US",
    "X-Request-ID": crypto.randomUUID(),
  },
});
Edit on GitHub

Last updated on

On this page