Zap Studio

Overview

A type-safe fetch wrapper with Standard Schema validation, convenient API methods, and custom error handling.

A type-safe fetch wrapper with Standard Schema validation.

Why fetch?

When fetching data from APIs, TypeScript can't verify that the response matches your expected type. You end up with unsafe type assertions that can cause runtime errors.

Before:

const response = await fetch("/api/users/1");
const data = await response.json();
const user = data as User; // 😱 Unsafe type assertion
// If the API returns { name: "John" } instead of { id: 1, name: "John" },
// your app breaks at runtime with no warning

After:

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

const user = await api.get("/api/users/1", UserSchema);
// ✨ Typed, validated, and safe!
// If the API response doesn't match UserSchema, you get a clear error

Features

  • Type-safe requests with automatic type inference
  • Runtime validation using Standard Schema (Zod, Valibot, ArkType, etc.)
  • Convenient API methods (GET, POST, PUT, PATCH, DELETE)
  • Factory pattern for creating pre-configured instances with base URLs
  • Custom error handling with FetchError and ValidationError classes
  • Full TypeScript support with zero configuration

Installation

npm install @zap-studio/fetch

Quick Start

Let's build a type-safe API client for a user management system.

1. Define Your Schema

First, define the shape of your API responses using any Standard Schema library:

import { z } from "zod";

// Define the user schema
const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  role: z.enum(["admin", "user", "guest"]),
  createdAt: z.string().transform((s) => new Date(s)),
});

type User = z.infer<typeof UserSchema>;

2. Make Type-Safe Requests

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

// Fetch a single user - fully typed and validated
const user = await api.get("https://api.example.com/users/1", UserSchema);

console.log(user.name);      // TypeScript knows this is a string
console.log(user.createdAt); // TypeScript knows this is a Date

3. Handle Errors Gracefully

import { FetchError, ValidationError } from "@zap-studio/fetch/errors";

try {
  const user = await api.get("https://api.example.com/users/1", UserSchema);
  console.log(`Hello, ${user.name}!`);
} catch (error) {
  if (error instanceof FetchError) {
    // HTTP error (404, 500, etc.)
    console.error(`API error: ${error.status}`);
  } else if (error instanceof ValidationError) {
    // Response didn't match schema
    console.error("Invalid API response:", error.issues);
  }
}
Edit on GitHub

Last updated on

On this page