fetch
A type-safe fetch wrapper that validates JSON responses at runtime with any Standard Schema validator.
fetch is a small fetch wrapper with Standard Schema response validation.
Motivation
Raw fetch does not throw on HTTP errors like 404 or 500 — you must check response.ok yourself. And it does not check that the JSON body matches what your code expects.
So most projects add the same pattern at every call site: check the status, parse the JSON, then validate it with schema.parse(await res.json()). It is easy to forget one of these steps somewhere, and that is how bad data or a network error goes unnoticed until it breaks something downstream.
fetch makes this pattern the default, not something you write by hand. api.get(url, UserSchema) checks the response, validates the body, and throws a clear, typed error — FetchError for a bad HTTP status, ValidationError for a bad shape — so you always know what went wrong and where.
It uses Standard Schema, so you are not locked into one validation library: start with Zod, move to Valibot later, and the call sites do not change. And because it only wraps the global fetch function, it is not a Node-only HTTP client — it runs the same way on Bun, Deno, Cloudflare Workers, and in the browser.
Features
- Raw fetch mode through
$fetch(input, options)— behaves like nativefetchand returns theResponse. - Validated fetch mode through
$fetch(input, schema, options)— parses and validates the JSON response. - HTTP method helpers through
api.get,api.post,api.put,api.patch, andapi.delete. - Configured clients through
createFetch(...)with sharedbaseURL, headers, query params, and error defaults. - JSON convenience through the
jsonoption, which serializes the request body and setsContent-Type. - Structured errors with
FetchErrorfor HTTP failures andValidationErrorfor schema failures. - Validator-agnostic — works with any library that implements Standard Schema.
- Optional logging through
createFetch({ logger })fromlogger— omit it and there’s zero logging overhead. - Native OpenTelemetry — a
CLIENTspan per request with trace context injected into outgoing headers, no-op until an SDK is registered. - Tree-shakeable — every export is a standalone function with no shared internal state; unused exports are dropped by any modern bundler.
Quick Start
import { ConsoleLogger } from "@zap-studio/logger";
import { createFetch } from "@zap-studio/fetch";
import { z } from "zod";
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.email(),
});
const logger = new ConsoleLogger({ minLevel: "debug" });
const { api } = createFetch({ logger });
const user = await api.get("https://api.example.com/users/1", UserSchema);
console.log(user.name); // typed as string, validated at runtime
Learn More
- Getting Started — install and make your first validated request.
- Raw Fetch Mode —
$fetchwithout a schema. - Validated Fetch Mode —
$fetchwith a schema. - HTTP Method Helpers —
api.get,api.post, and friends. - Configured Clients — shared defaults with
createFetch. - JSON Convenience — the
jsonrequest option. - Structured Errors —
FetchErrorandValidationError. - Validator-Agnostic — Standard Schema support.
- Logging — observe requests, responses, and validation failures.
- OpenTelemetry — native distributed tracing.
Runtime Support
| Runtime | Minimum version |
|---|---|
| Node.js | 18.0.0 (ships native fetch) |
| Bun | 1.0.0 |
| Deno | 1.42 |
| Cloudflare Workers | Any current release |
| Browsers | Latest evergreen (Chrome, Edge, Firefox, Safari) |
The package relies on the global fetch API and ships standard ESM only. Deno 1.42 is the first release that can install packages from JSR (deno add jsr:@zap-studio/fetch).