A better fetch API. Works on node, browser, and workers.
[!IMPORTANT]
You are on v2 (alpha) development branch. See v1 for v1 docs.
Install:
npx nypm i ofetch
Import:
import { ofetch } from "ofetch";
ofetch smartly parse JSON responses.
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.
// 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" });
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. ofetch will automatically set the duplex: "half" option for streaming support!
undefinedExample:undefined
const { users } = await ofetch("/api/users", {
method: "POST",
body: { some: "json" },
});
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.
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:
await ofetch("/url").catch((error) => error.data);
To bypass status error catching you can set ignoreResponseError option:
await ofetch("/url", { ignoreResponseError: true });
ofetch Automatically retries the request if an error happens and if the response status code is included in retryStatusCodes list:
undefinedRetry status codes:undefined
408 - Request Timeout409 - Conflict425 - Too Early (Experimental)429 - Too Many Requests500 - Internal Server Error502 - Bad Gateway503 - Service Unavailable504 - Gateway TimeoutYou 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.
await ofetch("http://google.com/404", {
retry: 3,
retryDelay: 500, // ms
retryStatusCodes: [404, 500], // response status codes to retry
});
You can specify timeout in milliseconds to automatically abort a request after a timeout (default is disabled).
await ofetch("http://google.com/404", {
timeout: 3000, // Timeout after 3 seconds
});
The response can be type assisted:
const article = await ofetch<Article>(`/api/article/${id}`);
// Auto complete working with article.id
baseURLBy using baseURL option, ofetch prepends it for trailing/leading slashes and query search params for baseURL using ufo:
await ofetch("/config", { baseURL });
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:
await ofetch("/movie?lang=en", { query: { id: 123 } });
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.
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.
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.
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.
await ofetch("/api", {
async onResponseError({ request, response, options }) {
// Log error
console.log(
"[fetch response error]",
request,
response.status,
response.body
);
},
});
If necessary, itβs also possible to pass an array of function that will be called sequentially.
await ofetch("/api", {
onRequest: [
() => {
/* Do something */
},
() => {
/* Do something else */
},
],
});
This utility is useful if you need to use common options across several fetch calls.
undefinedNote: Defaults will be cloned at one level and inherited. Be careful about nested options like headers.
const apiFetch = ofetch.create({ baseURL: "/api" });
apiFetch("/test"); // Same as ofetch('/test', { baseURL: '/api' })
By using headers option, ofetch adds extra headers in addition to the request default headers:
await ofetch("/movies", {
headers: {
Accept: "application/json",
"Cache-Control": "no-cache",
},
});
If you need to access raw response (for headers, etc), you can use ofetch.raw:
const response = await ofetch.raw("/sushi");
// response._data
// response.headers
// ...
As a shortcut, you can use ofetch.native that provides native fetch API
const json = await ofetch.native("/sushi").then((r) => r.json());
undefinedExample: Handle SSE response:
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);
}
In Node.js (>= 18) environments, you can provide a custom dispatcher to intercept requests and support features such as Proxy and self-signed certificates. This feature is enabled by undici built-in Node.js. read more about the Dispatcher API.
Some available agents:
ProxyAgent: A Proxy Agent class that implements the Agent API. It allows the connection through a proxy in a simple way. (docs)MockAgent: A mocked Agent class that implements the Agent API. It allows one to intercept HTTP requests made through undici and return mocked responses instead. (docs)Agent: Agent allows dispatching requests against multiple different origins. (docs)undefinedExample: Set a proxy agent for one request:
import { ProxyAgent } from "undici";
import { ofetch } from "ofetch";
const proxyAgent = new ProxyAgent("http://localhost:3128");
const data = await ofetch("https://icanhazip.com", { dispatcher: proxyAgent });
undefinedExample: Create a custom fetch instance that has proxy enabled:
import { ProxyAgent, setGlobalDispatcher } from "undici";
import { ofetch } from "ofetch";
const proxyAgent = new ProxyAgent("http://localhost:3128");
const fetchWithProxy = ofetch.create({ dispatcher: proxyAgent });
const data = await fetchWithProxy("https://icanhazip.com");
undefinedExample: Set a proxy agent for all requests:
import { ProxyAgent, setGlobalDispatcher } from "undici";
import { ofetch } from "ofetch";
const proxyAgent = new ProxyAgent("http://localhost:3128");
setGlobalDispatcher(proxyAgent);
const data = await ofetch("https://icanhazip.com");
undefinedExample: Allow self-signed certificates (USE AT YOUR OWN RISK!)
import { Agent } from "undici";
import { ofetch } from "ofetch";
// Note: This makes fetch unsecure against MITM attacks. USE AT YOUR OWN RISK!
const unsecureAgent = new Agent({ connect: { rejectUnauthorized: false } });
const unsecureFetch = ofetch.create({ dispatcher: unsecureAgent });
const data = await unsecureFetch("https://www.squid-cache.org/");
FetchOptions interfaceYou can augment the FetchOptions interface to add custom properties.
// 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.
const myFetch = ofetch.create({
onRequest(context) {
// ^? { ..., options: {..., requiresAuth?: boolean }}
console.log(context.options.requiresAuth);
},
});
myFetch("/foo", { requiresAuth: true });
π Published under the MIT license.
We use cookies
We use cookies to analyze traffic and improve your experience. You can accept or reject analytics cookies.