Skip to main content

What you get

ByteKit is most useful when you need more than a raw fetch wrapper.
  • A reusable ApiClient with typed request helpers
  • Response validation through schema adapters
  • Retry and circuit breaker primitives
  • A focused bytekit/async toolkit for concurrency and timing control
  • Helper modules for streaming, storage, logging, caching, and more

Install the package

npm install bytekit
pnpm add bytekit
yarn add bytekit

Pick an import style

Use the package root for convenience:
import { ApiClient, zodAdapter } from "bytekit";
Use focused entrypoints for clearer boundaries:
import { ApiClient } from "bytekit/api-client";
import { retry, parallel } from "bytekit/async";

Make your first request

import { ApiClient } from "bytekit/api-client";

const api = new ApiClient({
  baseUrl: "https://jsonplaceholder.typicode.com",
});

const users = await api.get("/users");
console.log(users);

Type your response

type User = {
  id: number;
  name: string;
  email: string;
};

const users = await api.get<User[]>("/users");

Add response validation

If you already use Zod or Valibot, you can validate responses at the edge of your app.
import { ApiClient, zodAdapter } from "bytekit";
import { z } from "zod";

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

const api = new ApiClient({
  baseUrl: "https://api.example.com",
});

const user = await api.get("/users/1", {
  validateResponse: zodAdapter(UserSchema),
});

Handle failures explicitly

import { ApiClient, ApiError } from "bytekit";

const api = new ApiClient({
  baseUrl: "https://api.example.com",
  locale: "en",
});

try {
  await api.get("/users/999");
} catch (error) {
  if (error instanceof ApiError) {
    console.error(error.status);
    console.error(error.message);
    console.error(error.body);
  }
}

Turn on resilience

import { ApiClient } from "bytekit/api-client";

const api = new ApiClient({
  baseUrl: "https://api.example.com",
  retryPolicy: {
    maxAttempts: 3,
    initialDelayMs: 200,
    backoffMultiplier: 2,
  },
  circuitBreaker: {
    failureThreshold: 5,
    timeoutMs: 30000,
  },
});

Use the async toolkit

import { parallel, retry } from "bytekit/async";

const result = await retry(fetchData, {
  maxAttempts: 4,
  baseDelay: 250,
});

const responses = await parallel(tasks, {
  concurrency: 3,
});

Common next steps

Learn the client model

Move from raw requests to reusable request architecture.

Add production resilience

Combine retries, rate limiting, caching, and request deduplication.

Control async workflows

Handle concurrency, retries, timeout, and debounce patterns.

Review exports

See what the package exposes before you standardize imports.

Minimal production template

import { ApiClient, createLogger } from "bytekit";

const logger = createLogger({ namespace: "app-api" });

export const api = new ApiClient({
  baseUrl: process.env.API_URL ?? "http://localhost:3000/api",
  timeoutMs: 10_000,
  logger,
  retryPolicy: {
    maxAttempts: 3,
    initialDelayMs: 200,
  },
  circuitBreaker: {
    failureThreshold: 5,
    timeoutMs: 30_000,
  },
});
Continue with the HTTP client guide if you want the core request model, or jump to resilience patterns for production setup.