Skip to content

Repository files navigation

ofetch

npm versionnpm downloads

A better fetch API. Works on node, browser, and workers.

Important

You are on v2 (alpha) development branch. See v1 for v1 docs.

Spoiler

πŸš€ Quick Start

Install:

npx nypm i ofetch

Import:

import{ofetch}from"ofetch";

βœ”οΈ Parsing Response

ofetch smartly parse JSON responses.

const{ users }=awaitofetch("/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 isawaitofetch("/movie?lang=en",{parseResponse: (txt)=>txt});// Get the blob version of the responseawaitofetch("/api/generate-image",{responseType: "blob"});// Get the stream version of the responseawaitofetch("/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. ofetch will automatically set the duplex: "half" option for streaming support!

Example:

const{ users }=awaitofetch("/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.

awaitofetch("https://google.com/404");// FetchError: [GET] "https://google/404": 404 Not Found// at async main (/project/playground.ts:4:3)

To catch error response:

awaitofetch("/url").catch((error)=>error.data);

To bypass status error catching you can set ignoreResponseError option:

awaitofetch("/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)
  • 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.

awaitofetch("http://google.com/404",{retry: 3,retryDelay: 500,// msretryStatusCodes: [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).

awaitofetch("http://google.com/404",{timeout: 3000,// Timeout after 3 seconds});

βœ”οΈ Type Friendly

The response can be type assisted:

constarticle=awaitofetch<Article>(`/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:

awaitofetch("/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:

awaitofetch("/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.

awaitofetch("/api",{asynconRequest({ request, options }){// Log requestconsole.log("[fetch request]",request,options);// Add `?t=1640125211170` to query search paramsoptions.query=options.query||{};options.query.t=newDate();},});

onRequestError({ request, options, error })

onRequestError will be called when the fetch request fails.

awaitofetch("/api",{asynconRequestError({ request, options, error }){// Log errorconsole.log("[fetch request error]",request,error);},});

onResponse({ request, options, response })

onResponse will be called after fetch call and parsing body.

awaitofetch("/api",{asynconResponse({ request, response, options }){// Log responseconsole.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.

awaitofetch("/api",{asynconResponseError({ request, response, options }){// Log errorconsole.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.

awaitofetch("/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.

constapiFetch=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:

awaitofetch("/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:

constresponse=awaitofetch.raw("/sushi");// response._data// response.headers// ...

🌿 Using Native Fetch

As a shortcut, you can use ofetch.native that provides native fetch API

constjson=awaitofetch.native("/sushi").then((r)=>r.json());

πŸ“‘ SSE

Example: Handle SSE response:

conststream=awaitofetch("/sse");constreader=stream.getReader();constdecoder=newTextDecoder();while(true){const{ done, value }=awaitreader.read();if(done)break;// Here is the chunked text of the SSE response.consttext=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.

Node.js

In Node.js (>= 18), you can use the dispatcher option with undici's ProxyAgent.

import{ProxyAgent}from"undici";constproxyAgent=newProxyAgent("http://localhost:3128");awaitofetch("https://icanhazip.com",{dispatcher: proxyAgent});

Example: Set proxy globally for all requests:

import{ProxyAgent,setGlobalDispatcher}from"undici";setGlobalDispatcher(newProxyAgent("http://localhost:3128"));

Example: Allow self-signed certificates (USE AT YOUR OWN RISK!)

import{Agent}from"undici";// Note: This makes fetch insecure against MITM attacks. USE AT YOUR OWN RISK!constunsecureAgent=newAgent({connect: {rejectUnauthorized: false}});awaitofetch("https://self-signed.example.com/",{dispatcher: unsecureAgent});

Bun and Deno

Bun supports the proxy option:

awaitofetch("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.

// Place this in any `.ts` or `.d.ts` file.// Ensure it's included in the project's tsconfig.json "files".declare module "ofetch"{interfaceFetchOptions{// Custom propertiesrequiresAuth?: boolean;}}export{};

This lets you pass and use those properties with full type safety throughout ofetch calls.

constmyFetch=ofetch.create({onRequest(context){// ^? { ..., options: {..., requiresAuth?: boolean }}console.log(context.options.requiresAuth);},});myFetch("/foo",{requiresAuth: true});

License

πŸ’› Published under the MIT license.

About

😱 A better fetch API. Works everywhere.

Resources

Stars

5.4k stars

Watchers

14 watching

Forks

Releases

Used by

Contributors

Languages