MCPcopy Index your code
hub / github.com/elbywan/wretch

github.com/elbywan/wretch @3.0.9

Chat with this repo
repository ↗ · DeepWiki ↗ · release 3.0.9 ↗ · + Follow
418 symbols 998 edges 80 files 86 documented · 21% updated 17d ago3.0.9 · 2026-06-19★ 5,176
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

wretch-logo Wretch github-badge npm-badge npm-downloads-badge Coverage Status license-badge

A tiny (~1.8KB g-zipped) wrapper built around fetch with an intuitive syntax.

f[ETCH] [WR]apper
##### Wretch 3.0 is now live 🎉 ! Check out the [Migration Guide](MIGRATION_V2_V3.md) for upgrading from v2, and please have a look at the [releases](https://github.com/elbywan/wretch/releases) and the [changelog](https://github.com/elbywan/wretch/blob/master/CHANGELOG.md) after each update for new features and breaking changes. ##### And if you like the library please consider becoming a [sponsor](https://github.com/sponsors/elbywan) ❤️. # Features #### `wretch` is a small wrapper around [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) designed to simplify the way to perform network requests and handle responses. - 🪶 **Small** - core is less than 1.8KB g-zipped - 💡 **Intuitive** - lean API, handles errors, headers and (de)serialization - 🧊 **Immutable** - every call creates a cloned instance that can then be reused safely - 🔌 **Modular** - plug addons to add new features, and middlewares to intercept requests - 🧩 **Isomorphic** - compatible with modern browsers, Node.js 22+, Deno and Bun - 🦺 **Type safe** - strongly typed, written in TypeScript - ✅ **Proven** - fully covered by unit tests and widely used - 💓 **Maintained** - alive and well for many years # Table of Contents - [**Quick Start**](#quick-start) - [**Motivation**](#motivation) - [**Installation**](#installation) - [**Compatibility**](#compatibility) - [**Usage**](#usage) - [**Recipes**](#recipes) - [**Api**](#api-) - [**Addons**](#addons) - [**Middlewares**](#middlewares) - [**Migration Guides**](#migration-guides) - [**License**](#license) # Quick Start
# 1️⃣ Install
npm i wretch
// 2️⃣ Import and create a reusable API client
import wretch from "wretch"

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

// 3️⃣ Make requests with automatic JSON handling
const post = await api.get("/posts/1").json()
console.log(post.title)

// 4️⃣ POST with automatic serialization
const created = await api
  .post({ title: "New Post", body: "Content", userId: 1 }, "/posts")
  .json()

// 5️⃣ Handle errors elegantly
await api
  .get("/posts/999")
  .notFound(() => console.log("Post not found!"))
  .json()

// 6️⃣ Different response types
const text = await api.get("/posts/1").text()      // Raw text
const response = await api.get("/posts/1").res()   // Raw Response object
const blob = await api.get("/photos/1").blob()     // Binary data
# Motivation #### Because having to write a second callback to process a response body feels awkward. Fetch needs a second callback to process the response body.
fetch("https://jsonplaceholder.typicode.com/posts/1")
  .then(response => response.json())
  .then(json => {
    //Do stuff with the parsed json
  });
Wretch does it for you.
// Use .res for the raw response, .text for raw text, .json for json, .blob for a blob …
wretch("https://jsonplaceholder.typicode.com/posts/1")
  .get()
  .json(json => {
    // Do stuff with the parsed json
    return json
  });
#### Because manually checking and throwing every request error code is tedious. Fetch won't reject on HTTP error status.
fetch("https://jsonplaceholder.typicode.com/posts/1")
  .then(response => {
    if(!response.ok) {
      if(response.status === 404) throw new Error("Not found")
      else if(response.status === 401) throw new Error("Unauthorized")
      else if(response.status === 418) throw new Error("I'm a teapot !")
      else throw new Error("Other error")
    }
    else {/* … */}
  })
  .then(data => {/* … */})
  .catch(error => { /* … */ })
Wretch throws when the response is not successful and contains helper methods to handle common codes.
wretch("https://jsonplaceholder.typicode.com/posts/1")
  .get()
  .notFound(error => { /* … */ })
  .unauthorized(error => { /* … */ })
  .error(418, error => { /* … */ })
  .res(response => {/* … */ })
  .catch(error => { /* uncaught errors */ })
#### Because sending a json object should be easy. With fetch you have to set the header, the method and the body manually.
fetch("https://jsonplaceholder.typicode.com/posts", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ "hello": "world" })
}).then(response => {/* … */})
// Omitting the data retrieval and error management parts…
With wretch, you have shorthands at your disposal.
wretch("https://jsonplaceholder.typicode.com/posts")
  .post({ "hello": "world" })
  .res(response => { /* … */ })
#### Because configuration should not rhyme with repetition. A Wretch object is immutable which means that you can reuse previous instances safely.
const token = "MY_SECRET_TOKEN"

// Cross origin authenticated requests on an external API
const externalApi = wretch("https://jsonplaceholder.typicode.com") // Base url
  // Authorization header
  .auth(`Bearer ${token}`)
  // Cors fetch options
  .options({ credentials: "include", mode: "cors" })
  // Handle 403 errors
  .resolve((w) => w.forbidden(error => { /* Handle all 403 errors */ }));

// Fetch a resource
const resource = await externalApi
  // Add a custom header for this request
  .headers({ "If-Unmodified-Since": "Wed, 21 Oct 2015 07:28:00 GMT" })
  .get("/posts/1")
  .json(() => {/* do something with the resource */});

// Post a resource
externalApi
  .url("/posts")
  .post({ "Shiny new": "resource object" })
  .json(() => {/* do something with the created resource */});
# Installation ## Package Manager
npm i wretch # or yarn/pnpm add wretch
## <script> tag The package contains multiple bundles depending on the format and feature set located under the `/dist/bundle` folder. Bundle variants > 💡 If you pick the core bundle, then to plug addons you must import them separately from `/dist/bundle/addons/[addonName].min.js` | Feature set | File Name | | ------------------ | ------------------- | | Core features only | `wretch.min.js` | | Core + all addons | `wretch.all.min.js` | | Format | Extension | | -------- | ---------- | | ESM | `.min.mjs` | | CommonJS | `.min.cjs` | | UMD | `.min.js` |



<script src="https://unpkg.com/wretch"></script>


<script type="module">
  import wretch from 'https://cdn.skypack.dev/wretch/dist/bundle/wretch.all.min.mjs'

  // … //
</script>
# Compatibility ## Browsers `wretch@^3` is compatible with modern browsers only. For older browsers please use `wretch@^1`. ## Node.js Wretch is compatible with and tested in _Node.js >= 22_. For older Node.js versions, please use `wretch@^2`. Node.js 22+ includes native fetch support and all required Web APIs (FormData, URLSearchParams, AbortController, etc.) out of the box, so no polyfills are needed. ## Deno Works with [Deno](https://deno.land/) out of the box.
deno add npm:wretch
import wretch from "wretch";

const text = await wretch("https://httpbingo.org").get("/status/200").text();
console.log(text); // -> { "code": 200, "description": "OK" }
## Bun Works with [Bun](https://bun.sh/) out of the box.
bun add wretch
import wretch from "wretch";

const text = await wretch("https://httpbingo.org").get("/status/200").text();
console.log(text); // -> { "code": 200, "description": "OK" }
# Usage ## Import
// ECMAScript modules
import wretch from "wretch"
// CommonJS
const wretch = require("wretch")
// Global variable (script tag)
window.wretch
## Common Use Cases
// 🌐 REST API Client
const api = wretch("https://jsonplaceholder.typicode.com")
  .auth("Bearer token")
  .resolve(r => r.json())

const users = await api.get("/users")
const user = await api.post({ name: "John" }, "/users")
users
// 📤 File Upload with Progress
import ProgressAddon from "wretch/addons/progress"
import FormDataAddon from "wretch/addons/formData"

await wretch("https://httpbingo.org/post")
  .addon([FormDataAddon, ProgressAddon()])
  .formData({ file: file })
  .post()
  .progress((loaded, total) => console.log(`${(loaded/total*100).toFixed()}%`))
  .json()
// 🔄 Auto-retry on Network Failure
import { retry } from "wretch/middlewares"

const resilientApi = wretch()
  .middlewares([retry({ maxAttempts: 3, retryOnNetworkError: true })])
// 🎯 Type-safe TypeScript
interface User { id: number; name: string; email: string }

const user = await wretch("https://jsonplaceholder.typicode.com")
  .get("/users/1")
  .json<User>() // Fully typed!

user
// 🔐 Automatic Token Refresh
const api = wretch("https://httpbingo.org/basic-auth/user/pass")
  .addon(BasicAuthAddon)
  .resolve(w => w.unauthorized(async (error, req) => {
    const newToken = await refreshToken()
    return req
    .basicAuth("user", "pass")
    .unauthorized(e => {
      console.log("Still unauthorized after token refresh");
      throw e
    })
    .fetch()
    .json()
  }))
## Custom Fetch Implementation You can provide a custom fetch implementation using the `.fetchPolyfill()` method. This is useful for for a variety of use cases including mocking, adding logging, timing, or other custom behavior to all requests made through a wretch instance.
import wretch from "wretch"

// Per-instance custom fetch
const api = wretch("https://jsonplaceholder.typicode.com")
  .fetchPolyfill((url, opts) => {
    console.log('Fetching:', url)
    console.time(url)
    return fetch(url, opts).finally(() => {
      console.timeEnd(url)
    })
  })

await api.get("/posts").json()
### [toFetch() - Fetch Adapter 🔗](https://elbywan.github.io/wretch/api/interfaces/index.Wretch#tofetch) Converts a wretch instance into a fetch-like function, preserving all configuration (middlewares, catchers, headers, etc.). Useful for integrating wretch with libraries that expect a fetch signature.
const myFetch = wretch("https://jsonplaceholder.typicode.com")
  .auth("Bearer token")
  .catcher(401, (err) => console.log("Unauthorized"))
  .toFetch()

// Use like regular fetch
const response = await myFetch("/users", { method: "GET" })
response
// Pass to libraries expecting fetch
import { createClient } from "@apollo/client"

const client = createClient({
  fetch: wretch().auth("Bearer token").toFetch()
})
## Chaining **A high level overview of the successive steps that can be chained to perform a request and parse the result.** ``` ┌─────────────────────────────────────────────────────────────────┐ │

Extension points exported contracts — how you extend this code

SnippetContext (Interface)
* Type for VM context with snippet globals
test/snippets/executor/sandbox.ts
AssertModule (Interface)
(no doc)
test/shared/wretch.spec.ts
Wretch (Interface)
(no doc)
src/types.ts
AbortWretch (Interface)
(no doc)
src/addons/abort.ts
ErrorFormatOptions (Interface)
(no doc)
test/snippets/formatter.ts
ExpectFn (Interface)
(no doc)
test/shared/wretch.spec.ts
WretchResponseChain (Interface)
(no doc)
src/types.ts
AbortResolver (Interface)
(no doc)
src/addons/abort.ts

Core symbols most depended-on inside this repo

expect
called by 177
test/bun/helpers.ts
get
called by 172
src/types.ts
expect
called by 60
test/node/helpers.ts
url
called by 45
src/types.ts
has
called by 40
test/snippets/plugin.ts
middlewares
called by 31
src/types.ts
post
called by 29
src/types.ts
json
called by 28
src/types.ts

Shape

Function 267
Method 92
Interface 43
Class 16

Languages

TypeScript100%

Modules by API surface

docs/api/assets/main.js68 symbols
src/types.ts27 symbols
src/core.ts25 symbols
test/snippets/plugin.ts24 symbols
test/snippets/assertions.ts17 symbols
test/shared/wretch.spec.ts16 symbols
test/snippets/runner.ts14 symbols
src/addons/progress.ts14 symbols
test/snippets/extractor.ts13 symbols
test/snippets/examples/logging-plugin.ts13 symbols
src/resolver.ts13 symbols
test/types/issue-313.ts9 symbols

Dependencies from manifests, versioned

@eslint/js10.0.1 · 1×
@fastify/basic-auth6.2.0 · 1×
@fastify/cors11.2.0 · 1×
@fastify/formbody8.0.2 · 1×
@fastify/multipart10.0.0 · 1×
@types/node25.8.0 · 1×
@web/dev-server-esbuild1.0.5 · 1×
@web/test-runner0.20.2 · 1×
@web/test-runner-playwright0.11.1 · 1×
c811.0.0 · 1×
conventional-changelog-cli2.2.2 · 1×
conventional-changelog-wretchfile:scripts/convent · 1×

For agents

$ claude mcp add wretch \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact