better-fetch

A type-safe fetch wrapper for TypeScript with Standard Schema validation, pre-defined routes, retries, and a plugin system.

Library
npm
v1.3.1
1,041stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
82/100Excellent
Development Activity92
Maintenance96
Community52
Maturity48
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
81/100Excellent
Architecture85
Code Quality80
Innovation75
Learning Curve85

Better Fetch wraps the native fetch API with runtime validation, typed responses, and an error-as-value result shape ({ data, error }) instead of throwing by default. It validates requests and responses against any Standard Schema-compliant library (Zod, Valibot, ArkType, or others), so teams aren’t locked into one validator, and it infers TypeScript types directly from those schemas.

Beyond validation, it adds the pieces most fetch wrappers hand-roll separately: a createFetch factory for building configured client instances, createSchema for pre-defining routes with typed input/output/query/params/headers, linear and exponential retry strategies, request timeouts, and a plugin system with request/response/success/error/retry hooks. It runs anywhere fetch does — browsers, Node 18+, Deno, Bun, and edge/worker runtimes.

What You Get

  • The betterFetch function for one-off typed requests with generic or Standard Schema-inferred response types
  • createFetch to build a reusable, pre-configured client with a shared baseURL, headers, retry policy, and plugins
  • createSchema to pre-define routes (including dynamic :param segments and @method modifiers) with input/output/query/params/header validation
  • Built-in linear and exponential retry strategies with custom shouldRetry conditions and request timeouts via AbortController
  • A plugin system with init hooks that can rewrite the URL/options before a request, plus onRequest/onResponse/onSuccess/onError/onRetry lifecycle hooks
  • An error-as-value response shape ({ data, error }) by default, or throw: true to raise a typed BetterFetchError instead

Common Use Cases

  • Building a typed SDK/client for an internal or third-party HTTP API without hand-writing response types
  • Sharing one configured fetch instance (base URL, auth headers, retry policy) across a frontend or backend codebase
  • Documenting and validating an API’s routes centrally via createSchema, catching request/response shape drift before it reaches the network
  • Adding resilience (timeouts, exponential backoff retries) to flaky third-party API calls without a bespoke retry loop
  • Extending fetch behavior (auth injection, logging, request rewriting) through composable plugins instead of ad hoc wrapper functions

Under The Hood

Architecture The library’s core betterFetch function (fetch.ts) is a single request pipeline: it resolves plugins and hooks via initializePlugins (plugins.ts), builds a stable request context object, runs onRequest hooks, issues the native fetch call with an AbortController-backed timeout, then walks a success or error branch that runs onResponse/onSuccess/onError hooks, optional Standard Schema validation, and an optional retry strategy (retry.ts) that recurses back into betterFetch on failure. createFetch (create-fetch/index.ts) layers a configured client on top by injecting an internal applySchemaPlugin, which resolves a pre-defined route from createSchema and merges its validated input/output/headers/params/query into the request options. The design cleanly separates request construction, plugin/hook orchestration, retry policy, and schema resolution into independent modules, so each concern (URL building in url.ts, header/body/timeout helpers in utils.ts) can be tested and swapped in isolation.

Tech Stack The package is pure TypeScript with zero runtime dependencies, relying only on the platform’s native fetch/Response/AbortController and an internal StandardSchemaV1 interface that lets any Standard Schema-compliant validator (Zod, Valibot, ArkType) plug in without a hard dependency on any one of them. It builds with tsdown to dual ESM/CJS output (dist/index.js / dist/index.cjs), lints and formats with Biome, and is versioned/released through bumpp and a GitHub Actions release workflow inside a pnpm workspace monorepo alongside a companion logger package and a Fumadocs-based documentation site.

Code Quality The package has an extensive Vitest test suite (five files, over 1,500 lines) covering the core fetch pipeline, createFetch/schema behavior, URL construction, and utility helpers, run through a vitest.workspace.ts across the monorepo. tsconfig.json enables strict mode plus exactOptionalPropertyTypes and noImplicitReturns for stronger type guarantees, and CI (ci.yml) runs build, typecheck, and test on every push and pull request. Source files carry extensive JSDoc comments on public option types, and errors are represented as a typed BetterFetchError class rather than swallowed or left as any.

What Makes It Unique Rather than binding to one validation library, Better Fetch is built against the Standard Schema specification, so the same request/response validation code works with Zod, Valibot, ArkType, or any other compliant validator interchangeably. Its createSchema/fetch-schema feature goes further than typical fetch wrappers by letting teams pre-define an API’s entire route surface, including dynamic path parameters and HTTP-method modifiers, as a single typed map that documents and validates the API centrally, rather than scattering ad hoc types across call sites.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search