ofetch

A better fetch API for Node, browsers, and workers with JSON parsing, retries, and typed errors built in.

Library
npm
v1.5.1
5,357stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
62/100Good
Development Activity60
Maintenance28
Community60
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
77/100Good
Architecture76
Code Quality78
Innovation62
Learning Curve90

ofetch wraps the native fetch API to remove the boilerplate that every project ends up rewriting by hand: it parses JSON responses automatically (falling back to blob/text/stream when the content type calls for it), stringifies JSON request bodies with sensible default headers, and throws a normalized FetchError with a readable message and a compact stack trace whenever a response isn’t OK. Failed requests to a configurable set of status codes (408, 409, 425, 429, 500, 502, 503, 504) are retried automatically, with support for custom retry counts, delays, and status code lists.

Beyond the request/response basics, ofetch adds baseURL and query/params merging via unjs/ufo, request/response/error lifecycle hooks (onRequest, onRequestError, onResponse, onResponseError), timeout support via AbortSignal, and an ofetch.create() factory for building a preconfigured instance that several calls can share. It runs unmodified across Node.js, browsers, and edge/worker runtimes, and exposes ofetch.raw and ofetch.native for callers who need the underlying Response object or an untouched native fetch.

As part of the unjs ecosystem, ofetch is the HTTP client used inside Nuxt, Nitro, and a number of other unjs packages, making it a common dependency for anyone building on those frameworks even when they never call it directly.

What You Get

  • Automatic response parsing that picks JSON, blob, text, or stream based on the response’s content type, with an optional custom parseResponse function.
  • Automatic JSON request body stringification, with the correct content-type/accept headers set only when they aren’t already present.
  • Built-in retry logic for a configurable list of status codes, with retry, retryDelay (static or a function of the request context), and safe defaults that avoid retrying payload methods unless explicitly opted in.
  • Normalized FetchError objects carrying data, status, statusText, request, and options, with internal stack frames hidden for a cleaner trace.
  • Lifecycle hooks — onRequest, onRequestError, onResponse, onResponseError — that can be single functions or arrays, run sequentially.
  • baseURL and query/params merging handled through unjs/ufo so relative paths and query strings compose correctly.
  • ofetch.create() for producing a pre-configured client with shared defaults, and ofetch.raw/ofetch.native for lower-level access.

Common Use Cases

  • Calling a JSON REST API from a Node.js server or serverless/edge function without hand-writing response parsing and error handling.
  • Building a typed API client wrapper (ofetch.create({ baseURL })) shared across many call sites in an app.
  • Adding automatic retry-with-backoff behavior for flaky upstream services without a separate retry library.
  • Streaming or handling binary responses (images, SSE, file downloads) where the default JSON assumption would be wrong.
  • Powering the HTTP layer inside a Nuxt/Nitro application, since ofetch is the default fetch client for both.

Under The Hood

Architecture The package is deliberately small and split by concern: src/fetch.ts implements createFetch(), which closes over a fetch implementation and returns the $fetch/ofetch callable plus .raw, .native, and .create methods; src/utils.ts holds the pure option-resolution and header-merging logic (resolveFetchOptions, mergeHeaders, callHooks); src/utils.url.ts handles baseURL/query composition via ufo; and src/error.ts builds the normalized FetchError using Object.defineProperty getters so error.data/error.status always reflect the live response/context rather than a stale copy. src/index.ts is the thin public entry point that wires a global-scope fetch into createFetch. The core request path in $fetchRaw is a single async function that threads a shared FetchContext (request, options, response, error) through hook calls, body normalization, the underlying fetch() call, response parsing, and error handling — every extension point (retry, hooks, error shape) is a branch over that one context object rather than a separate abstraction layer, so the whole request lifecycle is traceable in one file.

Tech Stack Written in TypeScript targeting ESM only (type: module, single ./dist/index.mjs entry), built with obuild, and published without runtime dependencies beyond ufo (used for URL/query joining) — Node’s undici and node-fetch-native are referenced only as optional/dev-time types for the dispatcher/agent options, not hard dependencies. Linting is via ESLint using eslint-config-unjs plus Prettier for formatting, and changelogen drives release/versioning. The library targets Node.js 18+, modern browsers, and edge/worker runtimes that implement the standard Fetch API, with no bundler-specific tooling required to consume it.

Code Quality Tests live in a single test/index.test.ts file run with Vitest (vitest run --coverage, using @vitest/coverage-v8), covering JSON/body handling, retries, timeouts, hooks, and error shapes against a real h3-based test server rather than mocks. Error handling is explicit throughout — createFetchError centralizes error construction instead of scattering throw statements, and the retry path distinguishes abort errors from retryable status codes rather than swallowing failures. Naming is consistent and the codebase is fully typed with no any escape hatches in the public API surface; CI runs lint, build, and the test suite with coverage across three Node versions (20/22/24) on every push and PR.

API Design The public surface is intentionally minimal: ofetch(url, options) behaves like fetch but resolves directly to parsed data, ofetch.raw returns the full response when headers/status are needed, ofetch.create() bakes in shared defaults for reuse across a codebase, and ofetch.native provides an escape hatch to unmodified fetch. Getting started requires no configuration — await ofetch('/api/users') is a complete example — and the options object composes gradually (baseURL, query, retry, timeout, hooks) rather than requiring a builder or class instantiation, which keeps the learning curve low for anyone already familiar with fetch.

Used by 7 apps in this directory

TypeScript
81%
Other

Directus

CMS · Low Code Platforms

37,783

Connect any SQL database and get instant REST and GraphQL APIs, a visual management Studio, and a native MCP server for AI agents — free for most organizations.

View details
92
Repo Health
82
Technical
76
Dependency
Built with
TypeScript81%
Vue18%
Updated 3 days ago
TypeScript
95%
Apache 2.0

Enclosed

Security

2,072

Send end-to-end encrypted notes and files where the server never sees your content — true zero-knowledge sharing.

View details
57
Repo Health
88
Technical
71
Dependency
Built with
TypeScript95%
Updated 4 days ago
TypeScript
97%
MIT

NewsNow

Bookmarks Archiving

21,642

Self-hostable news aggregator with an elegant column layout, GitHub OAuth, adaptive scraping intervals, and seamless Cloudflare D1 or SQLite persistence.

View details
66
Repo Health
74
Technical
72
Dependency
Built with
TypeScript97%
Updated 2 months ago
TypeScript
91%
MIT

open-pencil

AI Design Tools · Design Tools

8,197

An open-source design editor that reads native Figma files, ships a built-in AI assistant with 100+ design tools, and offers real-time serverless collaboration — all without giving up your files.

View details
82
Repo Health
78
Technical
69
Dependency
Built with
TypeScript91%
Updated yesterday
TypeScript
94%
AGPL 3.0

Papra

Bookmarks Archiving

5,296

Self-hosted document archiving with email ingestion, OCR full-text search, and pluggable storage — store once, find anything.

View details
86
Repo Health
82
Technical
69
Dependency
Built with
TypeScript94%
Updated yesterday
TypeScript
100%
AGPL 3.0

RSSHub

Automation · Social Media

46,052

Turn any website into an RSS feed — social media, streaming platforms, and niche sites all become subscribable in seconds.

View details
83
Repo Health
82
Technical
72
Dependency
Built with
TypeScript100%
Updated yesterday
JavaScript
84%
MIT

Unlighthouse

Developer Tools

4,798

An open-source CLI that scans your entire website with Google Lighthouse, using smart sampling and a modern dashboard UI to audit every page instead of one URL at a time.

View details
72
Repo Health
70
Technical
79
Dependency
Built with
JavaScript84%
TypeScript11%
Updated 3 weeks ago

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