Library / SDK
elbywan/wretch avatar
elbywan/wretch

Wretch 3.0: a 1.8KB fetch wrapper that throws on HTTP errors

A tiny wrapper built around fetch with an intuitive syntax. :candy:

5,178 stars110 forksTypeScriptMIT

At a glance

What is it?
Wretch is a small TypeScript wrapper around fetch that removes the second callback for body parsing and turns non-2xx responses into rejections. Here is what its API buys you, what it costs, and who should stay on plain fetch.
Who is it for?
Adopt Wretch if your codebase makes many fetch calls and you are tired of writing response.ok checks and response.json() chains by hand, and if you can run Node.js 22 or newer or a modern browser. Do not adopt it if you depend on fetch features the wrapper does not expose, or if you need a long-term support guarantee that a single-maintainer MIT library cannot give you.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 107 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The two callbacks Wretch removes from every fetch call

Plain fetch resolves as soon as response headers arrive. The body is still a stream, so you call response.json() or response.text() in a second step. That is one extra callback per request, and it is the reason the README opens its motivation section with the line "Because having to write a second callback to process a response body feels awkward." Wretch folds that step into the request chain: wretch(url).get().json() returns the parsed body directly, and .text(), .blob() and .res() cover the other shapes.

The second annoyance is error handling. Fetch does not reject on HTTP error status, so a 404 arrives as a resolved promise with response.ok set to false. Wretch rejects instead, and it adds named catchers for common codes. The README example chains .notFound(), .unauthorized(), .error(418, ...) and a final .catch() for anything uncaught. That is a real behavioural difference from fetch, not a cosmetic one, and it is the part that changes how your existing try/catch blocks behave when you migrate.

The target user is an application developer who writes a lot of HTTP calls against a JSON API and does not want a full client library. There is no interceptor framework to learn, no request config object to assemble, and no separate error type hierarchy to import.

Immutable instances, addons and middlewares in the Wretch 3.0 design

A Wretch instance is immutable. The README states that every call creates a cloned instance that can then be reused safely, so a base client built once with a base URL, default options and an Authorization header can be shared across a module without one call leaking configuration into the next.

The README's configuration example builds exactly that: a base URL, then chained configuration, then reuse. Because each chain step returns a new instance, you can derive a second client from the first, for instance one that adds a different header, and the original stays untouched. That is the mechanism behind the "configuration should not rhyme with repetition" argument in the motivation section.

On top of the core, the package exposes subpath exports. package.json declares "./all", "./addons" and "./addons/*" alongside the root entry, each with separate require and import conditions and its own type declarations. The README describes addons as plugins that add features and middlewares as functions that intercept requests. Keeping the root entry small is the point: the README puts the core at less than 1.8KB g-zipped, and the addon subpaths let you pull in only what you call.

The compatibility claim is broad but bounded. The README lists modern browsers, Node.js 22+, Deno and Bun, and package.json sets engines.node to ">=22". Node 20 and 18 are outside that range, which matters if you are pinned to an older runtime.

Installing Wretch and making a first request

The README's quick start begins with a single npm install. The package is published as wretch, and it is ESM-first: package.json sets "type": "module" and provides a CommonJS build under dist/cjs for require consumers.

bash
npm i wretch

The next step in the README creates a reusable client from a base URL and sets fetch options once. The example uses jsonplaceholder.typicode.com, a public test API, so you can run it without any credentials.

javascript
import wretch from "wretch"

const api = wretch("https://jsonplaceholder.typicode.com")
  .options({ mode: "cors" })

const post = await api.get("/posts/1").json()
console.log(post.title)

The call to .json() resolves to the parsed body, so post.title is available on the next line with no intermediate response object. The README's next snippet posts a plain object and relies on Wretch to set the method, the Content-Type header and the serialized body.

javascript
const created = await api
  .post({ title: "New Post", body: "Content", userId: 1 }, "/posts")
  .json()

Error handling is the last piece of the quick start. The README attaches a named catcher before the body parser, and the catcher runs only for that status code.

javascript
await api
  .get("/posts/999")
  .notFound(() => console.log("Post not found!"))
  .json()

If the request succeeds, the notFound callback is skipped and .json() parses the body. If it returns 404, the callback runs. Because the README's example URL points at a nonexistent post, this snippet is also a quick way to confirm that your error path works at all.

Where Wretch is the wrong tool

Wretch is a wrapper, not a client. It does not add retries, caching, request deduplication, a query-string builder or a response schema validator. If you need those, you are choosing between an addon, a middleware you write yourself, or a different library entirely. The README documents addons and middlewares as extension points but does not present a batteries-included feature set, and the recent release notes do not claim otherwise.

The runtime floor is the sharper constraint. package.json sets engines.node to ">=22", and the README's compatibility list repeats Node.js 22+. If your deployment target is an older Node LTS, Wretch 3.x is not the version for you, and the README does not document a supported fallback.

The version 3 line is also young relative to the project's history. The repository carries MIGRATION_V1_V2.md and MIGRATION_V2_V3.md, which tells you the API has broken twice across major versions, and the README points readers at the migration guide and the changelog after each update. That is normal for a library of this age, but it means an upgrade is a task, not a version bump.

Finally, if your codebase makes three fetch calls in total, the wrapper is overhead you will not earn back. The value shows up at volume, in files where the same error-handling boilerplate would otherwise repeat.

Wretch and axios: a difference in what gets wrapped

The obvious alternative is axios, and the difference is architectural rather than cosmetic. Axios ships its own HTTP adapter. In Node it uses the http and https modules rather than the platform fetch, and it brings its own request and response object model, its own interceptor system and its own error class. Wretch wraps the fetch implementation the runtime already provides, so the underlying transport is whatever your environment gives you, and the Response object you get from .res() is the platform Response.

That has practical consequences. Anything the platform fetch does, such as streaming a response body, is reachable through Wretch's raw response accessor but is not re-implemented by the library. Anything axios does beyond fetch, such as its progress events or its transform pipeline, has no direct Wretch equivalent. In the other direction, Wretch's bundle is small enough that the README describes the core as less than 1.8KB g-zipped, which is a different order of magnitude from a full client.

If you already use fetch everywhere and only want the ergonomics fixed, Wretch is the smaller change. If you need the features axios bundles, switching to Wretch means rebuilding them.

Maintenance, licence and the cost of staying on 3.x

The repository is not archived, and the last push was on 2026-06-19. Release 3.0.9 is dated the same day, 3.0.8 is dated 2026-05-23 and 3.0.7 is dated 2026-03-07, so the 3.x line has seen three patch releases in roughly three months. That cadence is patch-level: the release notes for these versions are not summarized in the repository README, and the README directs readers to the changelog and the releases page for new features and breaking changes after each update.

For upgrade cost, plan on reading the changelog before every minor or major bump. The presence of two migration guides in the repository root is the concrete signal: this library has broken its API across major versions before, and the README's own upgrade instruction is to consult the migration guide and the changelog. Pinning an exact version and reading the diff before moving is cheaper than discovering a renamed method in production.

The licence is MIT, stated in the README badge and in the LICENSE file at the repository root. MIT permits commercial and closed-source use with the copyright notice and permission notice retained. That is the general shape of the licence, not legal advice; if your organisation has a policy on third-party dependencies, run the LICENSE file past whoever owns that policy.

Editorial conclusion

Adopt Wretch if your codebase makes many fetch calls and you are tired of writing response.ok checks and response.json() chains by hand, and if you can run Node.js 22 or newer or a modern browser. Do not adopt it if you depend on fetch features the wrapper does not expose, or if you need a long-term support guarantee that a single-maintainer MIT library cannot give you. Before you commit, read MIGRATION_V2_V3.md if you are upgrading from version 2, and check the changelog for the release you pin, since the 3.x line has shipped breaking changes.

Frequently asked questions

How do I install Wretch?

Install it from npm with npm i wretch. The package is ESM-first, with a CommonJS build under dist/cjs for require consumers, and package.json sets engines.node to ">=22".

Does Wretch reject on HTTP error statuses like 404?

Yes. The README's motivation section states that fetch will not reject on HTTP error status, while Wretch throws when the response is not successful. It also provides named catchers such as .notFound(), .unauthorized() and .error(418, ...) for specific codes.

Which runtimes does Wretch support?

The README lists modern browsers, Node.js 22+, Deno and Bun as compatible, and package.json sets engines.node to ">=22". Older Node releases are outside the documented range.

What licence does Wretch use?

MIT. The README carries an MIT licence badge and the repository root contains a LICENSE file.

Official sources

  1. elbywan/wretch on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/elbywan-wretch.svg)](https://hysenlabs.com/projects/elbywan-wretch)