Skip to main content

Build a production-ready client

ByteKit lets you combine several resilience patterns without layering multiple runtime dependencies.
import { ApiClient, RateLimiter } from "bytekit";

const rateLimiter = new RateLimiter({
  maxRequests: 50,
  windowMs: 60_000,
});

const api = new ApiClient({
  baseUrl: "https://api.example.com",
  retryPolicy: {
    maxAttempts: 3,
    initialDelayMs: 200,
    backoffMultiplier: 2,
  },
  circuitBreaker: {
    failureThreshold: 5,
    timeoutMs: 30_000,
  },
  interceptors: {
    request: async (url, init) => {
      await rateLimiter.waitForAllowance(url);
      return [url, init];
    },
  },
});

Deduplicate concurrent requests

import { ApiClient, RequestDeduplicator } from "bytekit";

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

const [usersA, usersB] = await Promise.all([
  deduplicator.execute("/users", () => api.get("/users")),
  deduplicator.execute("/users", () => api.get("/users")),
]);
This is useful when several UI surfaces request the same resource at the same time.

Use RequestCache directly

import { ApiClient, RequestCache } from "bytekit";

const cache = new RequestCache({
  ttl: 10 * 60 * 1000,
  staleWhileRevalidate: 2 * 60 * 1000,
});

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

async function getStats() {
  const cached = cache.get("/stats");
  if (cached) return cached;

  const fresh = await api.get("/stats");
  cache.set("/stats", fresh);
  return fresh;
}
This pattern gives you a simple stale-while-revalidate workflow without coupling cache behavior to every request call.

Add structured visibility

import { ApiClient, createLogger } from "bytekit";

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

const api = new ApiClient({
  baseUrl: "https://api.example.com",
  logger,
  retryPolicy: {
    maxAttempts: 3,
  },
});
Pairing retries with structured logs makes failures easier to debug in production.