npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

arcesso

v1.0.3

Published

A modern, lightweight TypeScript HTTP client that's **zero dependencies** (13KB), **universal** (works in browsers and servers), and built on **native fetch**. Features Standard Schema validation, retry logic, timeout support, and comprehensive error hand

Readme

Arcesso

A modern, lightweight TypeScript HTTP client that's zero dependencies (13KB), universal (works in browsers and servers), and built on native fetch. Features Standard Schema validation, retry logic, timeout support, and comprehensive error handling.

Perfect for production: Tiny bundle, type-safe validation with Zod/Valibot, and works everywhere.

Why Arcesso?

Tiny & Fast: Only 13KB bundled, zero dependencies, built on native fetch Universal: Works in browsers, Node.js, Bun, Deno, Edge functions, and serverless Type-Safe: Full TypeScript support with Standard Schema validation (Zod, Valibot) Production-Ready: Comprehensive error handling, retries, timeouts, and auth helpers

Features

  • Zero Dependencies: No external dependencies, works with any Standard Schema validation library
  • Small Bundle: Only 13KB - perfect for performance-critical applications
  • Universal: Works everywhere - browsers, Node.js, Bun, Deno, serverless functions
  • Native Fetch: Built on the standard Fetch API, no legacy HTTP clients
  • Type-Safe HTTP Methods: Clean get, post, put, delete, patch methods with full TypeScript support
  • Complete Schema Pipeline: Validate request bodies, success responses, and error responses
    • Input Schema Validation: Validate request bodies before sending (works with Zod, Valibot, etc.)
    • Success Response Schema Validation: Type-safe API responses
    • Error Response Schema Validation: Validate error responses for robust error handling
  • Built-in Auth Helpers: Bearer tokens, API keys, and basic auth support
  • Query Parameters: Simple object-to-query-string conversion with type safety
  • Smart Retry Logic: Configurable retry with exponential or linear backoff
  • Timeout Support: Request timeouts with proper error handling
  • Callback-based Error Handling: Handle different error types with dedicated callbacks
  • Global Configuration: Set base URL, headers, and defaults once
  • Schema Validation for Callbacks: Validate callback return values with schemas

Installation

bun add arcesso
bun add zod # or valibot, arktype, etc.

13KB total bundle size - Arcesso adds almost no weight to your application!

Usage

Basic Usage

import { get, post } from "arcesso";
import { z } from "zod";

// Define schemas for the complete pipeline
const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
});

const CreateUserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().min(0),
});

const ErrorSchema = z.object({
  error: z.string(),
  code: z.number(),
});

// Clean, simple API with auth and query params
const user = await get("/api/users/1", {
  auth: { bearer: "token123" },
  schemas: {
    success: UserSchema,
    error: ErrorSchema,
  },
  onError: (error) => {
    console.error("Failed:", error.message);
    return null;
  },
  onHttpError: (errorData) => {
    // errorData is validated against ErrorSchema
    console.error(`API Error: ${errorData.error} (${errorData.code})`);
    return null;
  },
});

const users = await get("/api/users", {
  auth: { bearer: "token123" },
  query: { page: 1, limit: 10, active: true },
  schemas: {
    success: UserListSchema,
    error: ErrorSchema,
  },
});

const newUser = await post(
  "/api/users",
  {
    name: "John",
    email: "[email protected]",
    age: 25,
  },
  {
    auth: { bearer: "token123" },
    schemas: {
      input: CreateUserSchema, // Validates request body
      success: UserSchema, // Validates successful response
      error: ErrorSchema, // Validates error response
    },
    onHttpError: (errorData) => {
      console.error(`Creation failed: ${errorData.error}`);
      return null;
    },
  },
);

Authentication

Arcesso provides built-in auth helpers for common authentication methods:

// Bearer token (JWT, etc.)
const user = await get("/api/users/1", {
  auth: { bearer: "your-jwt-token" },
});

// API key
const user = await get("/api/users/1", {
  auth: { apiKey: "your-api-key" },
});

// Basic auth
const user = await get("/api/users/1", {
  auth: {
    basic: {
      username: "user",
      password: "pass",
    },
  },
});

// Combined with custom headers
const user = await get("/api/users/1", {
  auth: { bearer: "token123" },
  headers: { "X-Custom": "value" },
});

Query Parameters

Easily add query parameters to your requests:

// Simple query params
const users = await get("/api/users", {
  query: { page: 1, limit: 10 },
});
// → GET /api/users?page=1&limit=10

// Different value types
const users = await get("/api/users", {
  query: {
    page: 1, // number
    active: true, // boolean
    name: "John", // string
    optional: null, // filtered out
    missing: undefined, // filtered out
  },
});
// → GET /api/users?page=1&active=true&name=John

// Works with existing query params
const users = await get("/api/users?sort=name", {
  query: { page: 1, limit: 10 },
});
// → GET /api/users?sort=name&page=1&limit=10

Input Schema Validation

Arcesso can validate request bodies before sending them to the API:

import { post, ValidationError } from "arcesso";
import { z } from "zod";

const CreateUserSchema = z.object({
  name: z.string().min(1, "Name is required"),
  email: z.string().email("Invalid email format"),
  age: z.number().min(0, "Age must be positive"),
});

// Valid input - will be validated and sent
const user = await post("/api/users", {
  body: {
    name: "John",
    email: "[email protected]",
    age: 25,
  },
  inputSchema: CreateUserSchema,
  schema: UserSchema,
});

// Invalid input - will throw ValidationError before making request
try {
  await post("/api/users", {
    body: {
      name: "", // Invalid: empty string
      email: "not-an-email", // Invalid: no @ symbol
      age: -5, // Invalid: negative number
    },
    inputSchema: CreateUserSchema,
    schema: UserSchema,
  });
} catch (error) {
  if (error instanceof ValidationError) {
    console.error("Input validation failed:", error.message);
    // No HTTP request was made
  }
}

Error Response Schema Validation

Arcesso allows you to validate error responses with schemas, providing type-safe error handling:

import { get, HttpError } from "arcesso";
import { z } from "zod";

// Define schemas for both success and error responses
const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
});

const ApiErrorSchema = z.object({
  error: z.string(),
  code: z.number(),
  details: z.array(z.string()).optional(),
});

// Example 1: With error callback
const user = await get("/api/users/1", {
  schema: UserSchema,
  errorSchema: ApiErrorSchema,
  onHttpError: (errorData) => {
    // errorData is typed as { error: string; code: number; details?: string[] }
    console.error(`API Error: ${errorData.error} (${errorData.code})`);
    if (errorData.details) {
      console.error("Details:", errorData.details);
    }
    return null;
  },
});

// Example 2: Standard Promise behavior with typed errors
try {
  const user = await get("/api/users/1", {
    schema: UserSchema,
    errorSchema: ApiErrorSchema,
  });
} catch (error) {
  if (error instanceof HttpError) {
    // error.data contains the validated error response
    const errorData = error.data as { error: string; code: number };
    console.error(`API Error: ${errorData.error} (${errorData.code})`);
  }
}

// Example 3: Fallback behavior
// If error response doesn't match the schema, you get the raw Response object
const result = await get("/api/users/1", {
  schema: UserSchema,
  errorSchema: ApiErrorSchema,
  onHttpError: (errorData) => {
    if (typeof errorData === "object" && "error" in errorData) {
      // Validated error response
      console.error(`Structured error: ${errorData.error}`);
    } else {
      // Raw response (validation failed)
      console.error(`HTTP ${errorData.status}: ${errorData.statusText}`);
    }
    return null;
  },
});

Global Configuration

import { configure } from "arcesso";

configure({
  baseUrl: "https://api.example.com",
  headers: { Authorization: "Bearer token" },
  timeout: 5000,
  retry: { attempts: 3, backoff: "exponential" },
});

// Now all requests use the global config
const user = await get("/users/1", { schema: UserSchema });

Error Handling

const user = await get("/api/users/1", {
  schema: UserSchema,
  onError: (error) => {
    console.error("Request failed:", error.message);
    return null;
  },
  onTimeout: (error) => {
    console.error("Request timed out:", error.message);
    return { id: -1, name: "Timeout", email: "[email protected]" };
  },
  onValidationError: (error) => {
    console.error("Validation failed:", error.message);
    return { id: -1, name: "Invalid", email: "[email protected]" };
  },
});

Retry Logic

const data = await get("/api/data", {
  schema: DataSchema,
  retry: {
    attempts: 3,
    backoff: "exponential",
    initialDelay: 1000,
    maxDelay: 10000,
    onRetry: (attempt, error) => {
      console.log(`Retry ${attempt}: ${error.message}`);
    },
  },
});

Timeout Support

const data = await get("/api/data", {
  schema: DataSchema,
  timeout: 5000, // 5 second timeout
  onTimeout: (error) => {
    console.error("Request timed out:", error.message);
    return { fallback: "data" };
  },
});

Callback Schema Validation

import { z } from "zod";

const ProcessedDataSchema = z.object({
  success: z.boolean(),
  data: z.any(),
});

const result = await get("/api/data", {
  schema: DataSchema,
  onSuccess: {
    schema: ProcessedDataSchema,
    handler: (data) => ({ success: true, data }),
  },
  onError: {
    schema: ProcessedDataSchema,
    handler: (error) => ({ success: false, data: null }),
  },
});
// result is typed as { success: boolean; data: any }

Standard Promise Behavior

try {
  const user = await get("/api/users/1", { schema: UserSchema });
  console.log(user);
} catch (error) {
  console.error("Error:", error);
}

API Reference

HTTP Methods

// GET request
function get<T>(
  url: string,
  options: { schema: Schema<T> } & ArcessoRequestOptions,
): Promise<T>;
function get(url: string, options?: ArcessoRequestOptions): Promise<unknown>;

// POST request
function post<T>(
  url: string,
  options: { body: unknown; schema: Schema<T> } & ArcessoRequestOptions,
): Promise<T>;
function post(url: string, options?: ArcessoRequestOptions): Promise<unknown>;

// PUT, DELETE, PATCH follow the same pattern

Configuration

function configure(config: ArcessoConfig): void;

interface ArcessoConfig {
  baseUrl?: string;
  headers?: Record<string, string>;
  retry?: RetryOptions;
  timeout?: number;
}

Request Options

interface ArcessoRequestOptions<
  TSchema = unknown,
  TErrorSchema = unknown,
  TInputSchema = unknown,
> {
  schema?: Schema<TSchema>; // Validates successful response
  errorSchema?: Schema<TErrorSchema>; // Validates error response
  inputSchema?: Schema<TInputSchema>; // Validates request body
  body?: unknown;
  headers?: Record<string, string> | Headers;
  retry?: RetryOptions;
  timeout?: number;
  onSuccess?: CallbackOption<TSchema>;
  onError?: CallbackOption<Error>;
  onNetworkError?: CallbackOption<NetworkError>;
  onValidationError?: CallbackOption<ValidationError>;
  onHttpError?: CallbackOption<TErrorSchema>; // Validated error data or raw Response
  onTimeout?: CallbackOption<TimeoutError>;
}

Retry Options

interface RetryOptions {
  attempts: number;
  backoff?: "linear" | "exponential";
  initialDelay?: number;
  maxDelay?: number;
  retryOn?: number[]; // HTTP status codes to retry on
  onRetry?: (attempt: number, error: Error) => void;
}

Callback Types

// Simple callback
type SimpleCallback<T> = (input: T) => unknown;

// Callback with schema validation
interface CallbackWithSchema<T, S> {
  schema: S;
  handler: (input: T) => unknown;
}

type CallbackOption<T> = SimpleCallback<T> | CallbackWithSchema<T, Schema>;

Error Types

  • ValidationError - Thrown when validation fails
  • NetworkError - Thrown on network failures
  • HttpError - Thrown on HTTP errors (4xx, 5xx)
  • RetryExhaustedError - Thrown when retry attempts are exhausted
  • TimeoutError - Thrown when requests timeout

Validation Library Support

Arcesso implements the official Standard Schema specification locally with zero dependencies. This means it works with any validation library that implements Standard Schema:

| Library | Status | Bundle Impact | | ----------- | --------------- | ----------------------- | | Zod | ✅ Full support | You choose your version | | Valibot | ✅ Full support | You choose your version | | ArkType | ✅ Full support | You choose your version | | Yup | 🔄 Coming soon | - | | Joi | 🔄 Coming soon | - |

Zero Bundle Bloat: Arcesso includes the Standard Schema interface locally (1:1 copy of the official spec), so you get full validation support without any additional dependencies.

Example with different libraries:

// With Zod
import { z } from "zod";
const UserSchema = z.object({ name: z.string() });

// With Valibot
import * as v from "valibot";
const UserSchema = v.object({ name: v.string() });

// Both work the same way
const user = await get("/api/users/1", { schema: UserSchema });

Development

bun install
bun test

License

MIT