# ofetch
[](https://npmjs.com/package/ofetch)
[](https://npm.chart.dev/ofetch)
A better fetch API. Works on node, browser, and workers.
> [!IMPORTANT]
> You are on v2 (alpha) development branch. See [v1](https://github.com/unjs/ofetch/tree/v1) for v1 docs.
Spoiler
## 🚀 Quick Start
Install:
```bash
npx nypm i ofetch
```
Import:
```js
import { ofetch } from "ofetch";
```
## ✔️ Parsing Response
`ofetch` smartly parse JSON responses.
```js
const { users } = await ofetch("/api/users");
```
For binary content types, `ofetch` will instead return a `Blob` object.
You can optionally provide a different parser than `JSON.parse`, or specify `blob`, `arrayBuffer`, `text` or `stream` to force parsing the body with the respective `FetchResponse` method.
```js
// Return text as is
await ofetch("/movie?lang=en", { parseResponse: (txt) => txt });
// Get the blob version of the response
await ofetch("/api/generate-image", { responseType: "blob" });
// Get the stream version of the response
await ofetch("/api/generate-image", { responseType: "stream" });
```
## ✔️ JSON Body
If an object or a class with a `.toJSON()` method is passed to the `body` option, `ofetch` automatically stringifies it.
`ofetch` utilizes `JSON.stringify()` to convert the passed object. Classes without a `.toJSON()` method have to be converted into a string value in advance before being passed to the `body` option.
For `PUT`, `PATCH`, and `POST` request methods, when a string or object body is set, `ofetch` adds the default `"content-type": "application/json"` and `accept: "application/json"` headers (which you can always override).
Additionally, `ofetch` supports binary responses with `Buffer`, `ReadableStream`, `Stream`, and [compatible body types](https://developer.mozilla.org/en-US/docs/Web/API/fetch#body). `ofetch` will automatically set the `duplex: "half"` option for streaming support!
**Example:**
```js
const { users } = await ofetch("/api/users", {
method: "POST",
body: { some: "json" },
});
```
## ✔️ Handling Errors
`ofetch` Automatically throws errors when `response.ok` is `false` with a friendly error message and compact stack (hiding internals).
A parsed error body is available with `error.data`. You may also use `FetchError` type.
```ts
await ofetch("https://google.com/404");
// FetchError: [GET] "https://google/404": 404 Not Found
// at async main (/project/playground.ts:4:3)
```
To catch error response:
```ts
await ofetch("/url").catch((error) => error.data);
```
To bypass status error catching you can set `ignoreResponseError` option:
```ts
await ofetch("/url", { ignoreResponseError: true });
```
## ✔️ Auto Retry
`ofetch` Automatically retries the request if an error happens and if the response status code is included in `retryStatusCodes` list:
**Retry status codes:**
- `408` - Request Timeout
- `409` - Conflict
- `425` - Too Early ([Experimental](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Early-Data))
- `429` - Too Many Requests
- `500` - Internal Server Error
- `502` - Bad Gateway
- `503` - Service Unavailable
- `504` - Gateway Timeout
You can specify the amount of retry and delay between them using `retry` and `retryDelay` options and also pass a custom array of codes using `retryStatusCodes` option.
The default for `retry` is `1` retry, except for `POST`, `PUT`, `PATCH`, and `DELETE` methods where `ofetch` does not retry by default to avoid introducing side effects. If you set a custom value for `retry` it will **always retry** for all requests.
The default for `retryDelay` is `0` ms.
```ts
await ofetch("http://google.com/404", {
retry: 3,
retryDelay: 500, // ms
retryStatusCodes: [404, 500], // response status codes to retry
});
```
## ✔️ Timeout
You can specify `timeout` in milliseconds to automatically abort a request after a timeout (default is disabled).
```ts
await ofetch("http://google.com/404", {
timeout: 3000, // Timeout after 3 seconds
});
```
## ✔️ Type Friendly
The response can be type assisted:
```ts
const article = await ofetch(`/api/article/${id}`);
// Auto complete working with article.id
```
## ✔️ Adding `baseURL`
By using `baseURL` option, `ofetch` prepends it for trailing/leading slashes and query search params for baseURL using [ufo](https://github.com/unjs/ufo):
```js
await ofetch("/config", { baseURL });
```
## ✔️ Adding Query Search Params
By using `query` option (or `params` as alias), `ofetch` adds query search params to the URL by preserving the query in the request itself using [ufo](https://github.com/unjs/ufo):
```js
await ofetch("/movie?lang=en", { query: { id: 123 } });
```
## ✔️ Interceptors
Providing async interceptors to hook into lifecycle events of `ofetch` call is possible.
You might want to use `ofetch.create` to set shared interceptors.
### `onRequest({ request, options })`
`onRequest` is called as soon as `ofetch` is called, allowing you to modify options or do simple logging.
```js
await ofetch("/api", {
async onRequest({ request, options }) {
// Log request
console.log("[fetch request]", request, options);
// Add `?t=1640125211170` to query search params
options.query = options.query || {};
options.query.t = new Date();
},
});
```
### `onRequestError({ request, options, error })`
`onRequestError` will be called when the fetch request fails.
```js
await ofetch("/api", {
async onRequestError({ request, options, error }) {
// Log error
console.log("[fetch request error]", request, error);
},
});
```
### `onResponse({ request, options, response })`
`onResponse` will be called after `fetch` call and parsing body.
```js
await ofetch("/api", {
async onResponse({ request, response, options }) {
// Log response
console.log("[fetch response]", request, response.status, response.body);
},
});
```
### `onResponseError({ request, options, response })`
`onResponseError` is the same as `onResponse` but will be called when fetch happens but `response.ok` is not `true`.
```js
await ofetch("/api", {
async onResponseError({ request, response, options }) {
// Log error
console.log(
"[fetch response error]",
request,
response.status,
response.body
);
},
});
```
### Passing array of interceptors
If necessary, it's also possible to pass an array of function that will be called sequentially.
```js
await ofetch("/api", {
onRequest: [
() => {
/* Do something */
},
() => {
/* Do something else */
},
],
});
```
## ✔️ Create fetch with default options
This utility is useful if you need to use common options across several fetch calls.
**Note:** Defaults will be cloned at one level and inherited. Be careful about nested options like `headers`.
```js
const apiFetch = ofetch.create({ baseURL: "/api" });
apiFetch("/test"); // Same as ofetch('/test', { baseURL: '/api' })
```
## 💡 Adding headers
By using `headers` option, `ofetch` adds extra headers in addition to the request default headers:
```js
await ofetch("/movies", {
headers: {
Accept: "application/json",
"Cache-Control": "no-cache",
},
});
```
## 🍣 Access to Raw Response
If you need to access raw response (for headers, etc), you can use `ofetch.raw`:
```js
const response = await ofetch.raw("/sushi");
// response._data
// response.headers
// ...
```
## 🌿 Using Native Fetch
As a shortcut, you can use `ofetch.native` that provides native `fetch` API
```js
const json = await ofetch.native("/sushi").then((r) => r.json());
```
## 📡 SSE
**Example:** Handle SSE response:
```js
const stream = await ofetch("/sse");
const reader = stream.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
// Here is the chunked text of the SSE response.
const text = decoder.decode(value);
}
```
## 🕵️ Proxy Support
> [!IMPORTANT]
> **Environment Variables:** Bun and Deno respect `HTTP_PROXY` and `HTTPS_PROXY` environment variables. Node.js requires setting `NODE_USE_ENV_PROXY=1` to enable [built-in proxy support](https://nodejs.org/api/http.html#http_built_in_proxy_support).
### Node.js
In Node.js (>= 18), you can use the `dispatcher` option with [undici](https://undici.nodejs.org/)'s `ProxyAgent`.
```ts
import { ProxyAgent } from "undici";
const proxyAgent = new ProxyAgent("http://localhost:3128");
await ofetch("https://icanhazip.com", { dispatcher: proxyAgent });
```
**Example:** Set proxy globally for all requests:
```ts
import { ProxyAgent, setGlobalDispatcher } from "undici";
setGlobalDispatcher(new ProxyAgent("http://localhost:3128"));
```
**Example:** Allow self-signed certificates (USE AT YOUR OWN RISK!)
```ts
import { Agent } from "undici";
// Note: This makes fetch unsecure against MITM attacks. USE AT YOUR OWN RISK!
const unsecureAgent = new Agent({ connect: { rejectUnauthorized: false } });
await ofetch("https://self-signed.example.com/", { dispatcher: unsecureAgent });
```
### Bun and Deno
**Bun** supports the `proxy` option:
```ts
await ofetch("https://icanhazip.com", {
proxy: "http://localhost:3128",
});
```
**Deno** can also use undici with npm specifiers for programmatic configuration.
### 💪 Augment `FetchOptions` interface
You can augment the `FetchOptions` interface to add custom properties.
```ts
// Place this in any `.ts` or `.d.ts` file.
// Ensure it's included in the project's tsconfig.json "files".
declare module "ofetch" {
interface FetchOptions {
// Custom properties
requiresAuth?: boolean;
}
}
export {};
```
This lets you pass and use those properties with full type safety throughout `ofetch` calls.
```ts
const myFetch = ofetch.create({
onRequest(context) {
// ^? { ..., options: {..., requiresAuth?: boolean }}
console.log(context.options.requiresAuth);
},
});
myFetch("/foo", { requiresAuth: true });
```
## License
💛 Published under the [MIT](https://github.com/h3js/rou3/blob/main/LICENSE) license.