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

API Methods

Make schema-validated GET, POST, PUT, PATCH, and DELETE requests with the api method helpers.

api.get, api.post, api.put, api.patch, and api.delete are HTTP method helpers bound to $fetch. Use them when the HTTP method is known up front and the response should be validated.

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

Methods

Method HTTP method
api.get GET
api.post POST
api.put PUT
api.patch PATCH
api.delete DELETE

Each helper has the same overloads, options, and error behavior as $fetch, but injects its HTTP method for you. The injected method always wins — a method passed in options is overridden.

Read Data

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

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

const user = await api.get("https://api.example.com/users/1", UserSchema);

Send JSON

Use the json option for JSON request bodies. It is stringified with JSON.stringify and gets Content-Type: application/json when no content type is already set.

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

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

const user = await api.post("https://api.example.com/users", CreateUserResponseSchema, {
  json: {
    name: "Ada",
  },
});

CreateUserResponseSchema validates the response from the server. The json object is only the outgoing request body.

Use Native Body

Use native body when sending FormData, Blob, ReadableStream, or an already-serialized payload.

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

const ProfileSchema = z.object({
  id: z.string(),
  avatarUrl: z.string(),
});

const avatarBytes = new Uint8Array([137, 80, 78, 71]);
const avatar = new Blob([avatarBytes], { type: "image/png" });

const formData = new FormData();
formData.set("avatar", avatar, "avatar.png");

const profile = await api.patch("https://api.example.com/profile", ProfileSchema, {
  body: formData,
});

body and json cannot be used together — providing both throws a TypeError.

Query Params

searchParams accepts the same inputs as new URLSearchParams(...).

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

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

const page = await api.get("https://api.example.com/users", UserListSchema, {
  searchParams: {
    page: "1",
    limit: "20",
  },
});

Non-Throw Modes

HTTP errors and validation issues throw by default. You can disable either behavior per request.

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

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

// Does not throw FetchError on non-2xx responses
const user = await api.get("https://api.example.com/users/missing", UserSchema, {
  throwOnFetchError: false,
});

// Returns a Standard Schema result object instead of throwing ValidationError
const result = await api.get("https://api.example.com/users/1", UserSchema, {
  throwOnValidationError: false,
});

if (result.issues) {
  console.error(result.issues);
} else {
  console.log(result.value);
}

See Validation for details on the result object shape.

Raw Responses

The method helpers mirror the $fetch overloads, so calling one without a schema returns the native Response:

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

const response = await api.delete("https://api.example.com/sessions/current");

console.log(response.status);

For clarity, prefer $fetch directly when raw response handling is the main goal.

Last updated on September 15, 2026

Was this page helpful?