Giter VIP home page Giter VIP logo

ofetch's Introduction

ofetch

npm version npm downloads bundle Codecov License JSDocs

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

Spoiler

🚀 Quick Start

Install:

# npm
npm i ofetch

# yarn
yarn add ofetch

Import:

// ESM / Typescript
import { ofetch } from "ofetch";

// CommonJS
const { ofetch } = require("ofetch");

✔️ Works with Node.js

We use conditional exports to detect Node.js and automatically use unjs/node-fetch-native. If globalThis.fetch is available, will be used instead. To leverage Node.js 17.5.0 experimental native fetch API use --experimental-fetch flag.

keepAlive support

By setting the FETCH_KEEP_ALIVE environment variable to true, an HTTP/HTTPS agent will be registered that keeps sockets around even when there are no outstanding requests, so they can be used for future requests without having to re-establish a TCP connection.

Note: This option can potentially introduce memory leaks. Please check node-fetch/node-fetch#1325.

✔️ Parsing Response

ofetch will smartly parse JSON and native values using destr, falling back to the text if it fails to parse.

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 destr, or specify blob, arrayBuffer, or text to force parsing the body with the respective FetchResponse method.

// Use JSON.parse
await ofetch("/movie?lang=en", { parseResponse: JSON.parse });

// 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" });

✔️ 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 } = 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.

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 });

✔️ 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
  • 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.

await ofetch("http://google.com/404", {
  retry: 3,
  retryDelay: 500, // ms
});

✔️ Timeout

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
});

✔️ Type Friendly

The response can be type assisted:

const article = await ofetch<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:

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:

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.

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
    );
  },
});

✔️ 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.

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:

await ofetch("/movies", {
  headers: {
    Accept: "application/json",
    "Cache-Control": "no-cache",
  },
});

💡 Adding a HTTP(S) / Proxy Agent

If you need use a HTTP(S) / Proxy Agent, you can (for Node.js only):

Node >= v18

Add ProxyAgent to dispatcher option with undici

import { ofetch } from 'ofetch'
import { ProxyAgent } from 'undici'

await ofetch("/api", {
  dispatcher: new ProxyAgent("http://example.com"),
});

Node < v18

Add HttpsProxyAgent to agent option with https-proxy-agent

import { HttpsProxyAgent } from "https-proxy-agent";

await ofetch("/api", {
  agent: new HttpsProxyAgent("http://example.com"),
});

🍣 Access to Raw Response

If you need to access raw response (for headers, etc), can use ofetch.raw:

const response = await ofetch.raw("/sushi");

// response._data
// response.headers
// ...

Native fetch

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

const json = await ofetch.native("/sushi").then((r) => r.json());

📦 Bundler Notes

  • All targets are exported with Module and CommonJS format and named exports
  • No export is transpiled for the sake of modern syntax
    • You probably need to transpile ofetch, destr, and ufo packages with Babel for ES5 support
  • You need to polyfill fetch global for supporting legacy browsers like using unfetch

❓ FAQ

Why export is called ofetch instead of fetch?

Using the same name of fetch can be confusing since API is different but still, it is a fetch so using the closest possible alternative. You can, however, import { fetch } from ofetch which is auto-polyfill for Node.js and using native otherwise.

Why not have default export?

Default exports are always risky to be mixed with CommonJS exports.

This also guarantees we can introduce more utils without breaking the package and also encourage using ofetch name.

Why not transpiled?

By transpiling libraries, we push the web backward with legacy code which is unneeded for most of the users.

If you need to support legacy users, you can optionally transpile the library in your build pipeline.

License

MIT. Made with 💖

ofetch's People

Contributors

0xflotus avatar alex-key avatar arunanshub avatar atinux avatar autofix-ci[bot] avatar barbapapazes avatar chrstnfrrs avatar codebender828 avatar danielroe avatar daniluk4000 avatar dargmuesli avatar dennisbetawerk avatar escral avatar hebilicious avatar hmingv avatar huynl-96 avatar ilyasemenov avatar jamesm131 avatar johannschopplich avatar kevcodez avatar matthewpull avatar mini-ghost avatar mod7ex avatar nozomuikuta avatar okxiaoliang4 avatar pi0 avatar picunada avatar renovate[bot] avatar silverbackdan avatar webfansplz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ofetch's Issues

Empty body is converted to `''`

While migrating an app to ohmyfetch, one assertion on my test suite started to fail on a rather surprising way: the library return "" for responses with empty body (e.g. typical DELETE responses) instead of null or undefined.

If I follow the documentation, I can see why: the library will try to parse the response as JSON as default and, if it fails, as string. Them a body with length == 0 is parsed as an empty string.

While I can understand this behavior, it sounded counter-intuitive to me to return some value if the server intended to return nothing.

If you think this is by-design and not a bug, feel welcome to close it.

[Docs Request] Migration from using Axios / Service Worker Caching

As I'm finding the need for caching axios API calls and seeing that that Service Workers can't access those calls, I am wondering if using ohmyfetch for API calls (and integrating it with workbox runtimeCaching) is a better solution moving forward.

As part of migrations from Vue 2 / Nuxt 2, it would be extremely helpful to know how/why/when to use ohmyfetch instead of axios, and the process by which one might cache calls made using runtimeCaching.

$fetch using `globalThis` (not the polyfill)

Code seems to be doing the polyfill detection, but then $fetch still uses globalThis (and not the polyfilled variable; Pv in this case.)

This is macOs high sierra, safari 11 using a prod nuxt3 build (using browserstack to test)

image

FormData support

Seems a good idea for using [object FormData] to append proper content-type (adding in short)

Originally posted by @pi0 in #36 (comment)

Other notes:

  • validate formdata content or ensure we're not in browser environment to avoid setting header when sending files/blobs
  • Content-Disposition header

ExperimentalWarning: The Fetch API is an experimental feature.

Hi! I created error, but i dont understand why node js down?

            const url = 'http://api.weatherapi.com/v1/forecast.json?key=11acc57138bfc45a7abc190229221306&q='+ 'Miami' +'&days=1&aqi=no&alerts=no'
            await $fetch(url, {
                async onResponseError({ request, options, error }) {
                    console.log('ERROR')
                },
                async onResponse({ request, response, options }) {
                    resolve(response)
                }
            })

console

(node:12836) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
node:internal/process/task_queues:95
    runMicrotasks();
    ^

FetchError: 401 Unauthorized (http://api.weatherapi.com/v1/forecast.json?key=11acc57138bfc45a7abc190229221306&q=Miami&days=1&aqi=no&alerts=no)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async D:\js\megadub_server\server\ts\weather\index.js:47:13

Node.js v18.2.0
[nodemon] app crashed - waiting for file changes before starting...

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/autofix.yml
  • actions/checkout v4
  • actions/setup-node v4
  • autofix-ci/action ff86a557419858bb967097bfc916833f5647fa8c
.github/workflows/ci.yml
  • actions/checkout v4
  • actions/setup-node v4
  • codecov/codecov-action v4
npm
package.json
  • destr ^2.0.3
  • node-fetch-native ^1.6.4
  • ufo ^1.5.4
  • @types/node ^20.14.12
  • @vitest/coverage-v8 ^1.6.0
  • changelogen ^0.5.5
  • eslint ^9.7.0
  • eslint-config-unjs ^0.3.2
  • fetch-blob ^4.0.0
  • formdata-polyfill ^4.0.10
  • h3 ^1.12.0
  • jiti ^1.21.6
  • listhen ^1.7.2
  • prettier ^3.3.3
  • std-env ^3.7.0
  • typescript ^5.5.4
  • unbuild 2.0.0
  • vitest ^1.6.0
  • pnpm 9.6.0

  • Check this box to trigger a request for Renovate to run again on this repository

got error when 0.4.x is used in jest test. Error: You need to run with a version of node that supports ES Modules in the VM API.

When upgrade to 0.4.x, the jest test gives error: Error: You need to run with a version of node that supports ES Modules in the VM API.
`You need to run with a version of node that supports ES Modules in the VM API. See https://jestjs.io/docs/ecmascript-modules

  2 |
  3 | export async function getFile() {
> 4 |   const result = await $fetch('http://google.com');
    |                        ^
  5 |   return result;
  6 | }
  7 |

  at invariant (node_modules/jest-runtime/build/index.js:2423:11)
  at getExport (node_modules/ohmyfetch/cjs/node.cjs:1:136)
  at node_modules/ohmyfetch/cjs/node.cjs:2:47
  at getFile (src/index.js:4:24)
  at Object.<anonymous> (src/index.test.js:3:24)`

The same test works fine in 0.3.2

node v16.13.0
yarn 1.22.15
The test project is attached.
fetch-app.zip

Failed import from `ohmyfetch` in `nuxt`

Hello, we are trying to replace the dependency axios with ohmyfetch in a Nuxt module.

I get the two error messages when importing:
image

It doesn't matter if I want to import via require or import.

const { $fetch } = require('ohmyfetch');
// OR
const { $fetch } = await import('ohmyfetch');

I also tried to include ohmyfetch and ufo in the transpile. Unfortunately without success.


Node: v16.15.1
Nuxt: 2.15.8

Set header X-XSRF-TOKEN from Cookie XSRF-TOKEN

Hey there.

According to convenience, many libraries and frameworks, like axios and angular, automaticly passing headers X-XSRF-TOKEN from existing cookie XSRF-TOKEN.

For example, if i have cookie XSRF-TOKEN=AAAAAAAAMLand making ajax request, library should set header X-XSRF-TOKEN=AAAAAAAAML

Is it possible to make it ?

Timeout feature

Timeout support (limiting the request execution time) would allow for snappy client-facing network timeout error messages if network connectivity drops.

Auto retry on 4xx errors

Currently it automatically retries the GET request if there is a error. If it is 4xx error, it's not the valid request and therefor shouldn't try again.

`ohmyfetch` with `jest`

Hello,

have found a problem with jest and ohmyfetch. I came across this when using @nuxt/image in jest.

Is this perhaps due to the cjs build? There a mjs is loaded by dynamic import.

I created an example repository for this case.

Gives this error message and from time to time SEGMENT FAULT.

Ohne Titel 6

Thanks!


I have updated our test again, replaced jest with vitest.
Now it works, but you should think that ohmyfetch also works in jest?

Transpile nuxt2 module

I'm creating a nuxt module for both: nuxt2 and nuxt3. However I'm having some trouble by building a nuxt2 project by using nuxt generate. The module is using the OhMyFetch module which is not transpiled. Does anyone know what I have to do to get this fixed? I'm getting these kind of import errors:

Basically what I do is the following:

  1. I do use the https://github.com/nuxt/module-builder template.
  2. The build nuxt-module-build is going correctly and all files are generated.
  3. At this point I use npm publish for github package registry
  4. When adding the (build)module I'm getting some node-fetch / ohmyfetch related errors:

Can't resolve 'node:buffer' Can't resolve 'node:http' ...

Also I do have tried the build.transpile: ['module-name] config. Any thoughts? Thanks 😄

rename `params` to `query`

params can have different meanings in different contexts. fetch(URL, { query } is probably more readable.

TypeError: Error.captureStackTrace is not function

In fetch.ts on line 64 (https://github.com/unjs/ohmyfetch/blob/main/src/fetch.ts#L64), called when an error occurs during the fetch, a call is made to Error.captureStackTrace. This is a non-standard, V8-specific function that appears to work in Chrome and Nodejs, but does not work in Firefox.

This means that all error-handling is broken when using the Firefox browser, as my user code can no longer receive a FetchError if an issue occurs, but always receives this TypeError (without any of the information, such as response body).

Essentially, this makes ohmyfetch unusable in production code that supports the Firefox browser, or any non-Chromium-based browser.

I would also note that other projects have dealt with this issue by using Error().stack in some fashion - here is an example from 2015: trentm/node-bunyan#224

Please can this issue be resolved as a priority as, again, in its current form this otherwise helpful library cannot be used in general production scenarios.

The object created by the class becomes '[object Object]' in body, not JSON

Hi!

class SomeClass {
  x: number = 5;
}

const obj = new SomeClass();

console.log(obj); // SomeClass {x: 5}

function handleClick() {
  $fetch('http://localhost:7500/api/someurl', {
    method: 'POST',
    body: obj, // converted to string '[object Object]'
  })
  .then((result) => {
    console.log(result);
  })
  .catch((err) => {
    console.log(err);
  });
}

$fetch sends '[object Object]', not JSON.

Such ugly solution solves the problem:

  $fetch('http://localhost:7500/api/someurl', {
    method: 'POST',
    body: JSON.parse(JSON.stringify(obj)),
  })

Other solution of stange problem:

body: { ...obj },

Type error when `responseType` is not 'json'

Setting the responseType throws a type error.

env version
OS Windows 11(Build 22000.376)
Node v16.13.0
PNPM v6.23.6
ohmyfetch v0.4.13

My code:

const iconSVG = await $fetch<ArrayBuffer>(
    `${icon}.svg`,
    {
      method: 'GET',
      baseURL: 'https://api.iconify.design/',
      params: req.query,
      responseType: 'arrayBuffer'
    }
  ).catch((e) => {
    console.error(e)
    return error(res, 503)
  })

type check:

$ tsc --noEmit
api/[iconify].ts:27:7 - error TS2322: Type '"arrayBuffer"' is not assignable to type '"json"'.

27       responseType: 'arrayBuffer'
         ~~~~~~~~~~~~

  node_modules/.pnpm/[email protected]/node_modules/ohmyfetch/dist/error-b87bcb5b.d.ts:37:5
    37     responseType?: R;
           ~~~~~~~~~~~~
    The expected type comes from property 'responseType' which is declared here on type 'FetchOptions<"json">'


Found 1 error.

Impossible to send `x-www-form-urlencoded` payload the fetch way

Hi! The following requests fails to send x-www-form-urlencoded payload:

import { URLSearchParams } from 'url';
import { $fetch, fetch } from 'ohmyfetch';

(async function main() {
  const body = new URLSearchParams({ test: 'true' });

  const responseA = await $fetch('https://httpbin.org/post', { method: 'POST', body });
  console.log(responseA);

  const responseB = await fetch('https://httpbin.org/post', { method: 'POST', body });
  console.log(await responseB.json());
})();
// $fetch
{
  args: {},
  data: '{}',
  files: {},
  form: {},
  headers: {
    Accept: 'application/json',
    'Accept-Encoding': 'gzip,deflate,br',
    'Content-Length': '2',
    'Content-Type': 'application/json',
    Host: 'httpbin.org',
    'User-Agent': 'node-fetch',
    'X-Amzn-Trace-Id': 'Root=1-61a56083-3dd9c60f2746207123123123'
  },
  json: {},
  origin: '123.123.123.123',
  url: 'https://httpbin.org/post'
}
// fetch
{
  args: {},
  data: '',
  files: {},
  form: { test: 'true' },
  headers: {
    Accept: '*/*',
    'Accept-Encoding': 'gzip,deflate,br',
    'Content-Length': '9',
    'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
    Host: 'httpbin.org',
    'User-Agent': 'node-fetch',
    'X-Amzn-Trace-Id': 'Root=1-61a56084-557df690674d50c123123123'
  },
  json: null,
  origin: '123.123.123.123',
  url: 'https://httpbin.org/post'
}

Why is this important? Fetch spec allows for passing URLSearchParams to indicate x-www-form-urlencoded payload. node-fetch follows the spec. ohmyfetch does not, despite sending the user to node-fetch documentation for references.

Why it fails? Probably typeof (new URLSearchParams) === 'object' here

Option to avoid OPTIONS request before making due to CORS?

I'm attempting to make a POST request to a back-end API with a JSON body using $fetch (through Nuxt 3's useLazyFetch). This automatically triggers an OPTIONS request before making the POST request, due to CORS checks I am guessing. Is there a parameter I can set to avoid the OPTIONS request first?

In case of a negative answer, could we make this possible?

Thanks!

[Interceptors] Creating an API client with automatic token refresh functionality

Hi!

We talked about this yesterday over on the Nuxt discord's #nuxt3 channel, but since it's more of an ohmyfetch thing, I decided to create the issue here instead of the nuxt/framework repo.

We're currently firing up a development effort for a new Nuxt3 project in our company (we've previously used Nuxt2 in some projects). Axios was the preferred way of doing API requests previously but since ohmyfetch is now the recommended choice, I thought of rewriting our API client wrapper to use ohmyfetch instead of Axios.

I'm sure the problem is familiar to most if not all of you: how to write an API client that can automatically refresh your access tokens behind the scenes? This is pretty much our previous solution that uses Axios' interceptors:

const createAPIClient = () => {
  const apiClient = axios.create({ headers, baseUrl })
  
  apiClient.interceptors.request.use(config => {
    const accessTokens = authStore.tokens  // { accessToken: '', refreshToken: '' }
    if (tokens) {
      config.headers.common.Authorization = `Bearer ${tokens.accessToken}`
    }
    return config
  })
  
  apiClient.interceptors.response.use(
    response => response,
    async error => {
      const originalRequest = error.config
      
      // .isRetry is a non-axios property we use to differentiate the actual request from the one we're firing in this interceptor
      if (error.response?.status === 401 && !originalRequest.isRetry) {
        originalRequest.isRetry = true
        try {
          // fetch new tokens from our API
          const refreshToken = authStore.refreshToken
          const { data } = axios.post('/our/nuxt/server-middleware/auth/route/that/proxies/to/our/api/', { refreshToken })
          
          // simplified for brevity
          setNewAccessTokensToStore(data)
          
          // retry the original request
          originalRequest.headers = { ...originalRequest.headers, Authorization: `Bearer ${data.refreshToken}` }
          return apiClient(originalRequest)
        } catch (refreshError) {
          return Promise.reject(refreshError)
        }  
      }
    }
  )
  
  return apiClient
}

Historically we've done this by creating one larger doRequest function that gets called recursively, but it was refactored to use interceptors.

I'd like to replicate this functionality using ohmyfetch, so that I could do something like:

const $customFetch = () => {
  const client = $fetch.create({
    baseURL: 'https://example.com',
    async onRequest(ctx => {
      // like apiClient.interceptors.request.use
    }),
    async onResponseError(ctx => {
      // like apiClient.interceptors.response.use
    })
  })

// in any Nuxt3 component
const { data, pending, error, refresh } = await useAsyncData('getSomeData', () => $customFetch('/some/endpoint/in/our/api'))

and the access tokens get set to request headers and possibly refreshed automatically.

I was checking out ohmyfetch's interceptors, but apparently they don't return anything, only functioning as hooks where you can set up logging or do something else, instead of acting as sort of middleware like Axios' counterparts. This leads me to the conclusion that this probably isn't currently possible with ohmyfetch? It's no problem to use the recursive approach we had previously, but I feel that this is a bit of a shortcoming with the current interceptors.

Blob method does not exist in the result

How can I return the result in blob? It seems that .blob() does not exist

- Operating System: `Darwin`
- Node Version:     `v16.13.1`
- Nuxt Version:     `3.0.0-27319101.3e82f0f`
- Package Manager:  `[email protected]`
- Bundler:          `Vite`
- User Config:      `css`, `watch`, `build`
- Runtime Modules:  `-`
- Build Modules:    `-`
const {data: banners, refresh: bannersFetcher} = await useAsyncData(
    'banners',
    async () => {
      const banners = await $fetch('/api/mc/banners')
        .then((data) => {
          return Promise.all(_.map(data, async (banner) => {
            const blob = await $fetch(banner.data).then(res => res.blob())
            console.log('Hey', blob)
          }))
        })
      // console.log(banners)
      return []
    }
)

JSON headers is not working for array of objects

Hi there.
I was working with this library and I found out in a special case, it doesn't add header content-type: application/json to the request.

What I did

$fetch("/api/endpoint", {method: "POST", body: [{num: 42}, {num: 43}]})

What I expected

Automatically add content-type: application/json header and stringify the body.

What actually happened

the content-type: application/json header was not added and the request body replaced with [object Object],[object Object]

Details

The request was not contain content-type: application/json header because as this feature implemented here, the toString() of the body in this case is not equal to [object Object] (actually it is equal to [object Object],[object Object] in this case). So automatic json headers and body stringify didn't happen.

use $fetch in SSR leading to "Headers is not a function"

I found that today after upgrade Nuxt3 to lastest, when use $fetch within SSR leading to error:

TypeError: Headers is not a constructor
    at $fetchRaw2 (file:///workspace/node_modules/ohmyfetch/dist/chunks/fetch.mjs:122:33)
    at $fetch2 (file:///workspace/node_modules/ohmyfetch/dist/chunks/fetch.mjs:158:12)
    at file:///workspace/.nuxt/dist/server/server.mjs:58925:25
    at Object.asyncData.refresh (file:///workspace/.nuxt/dist/server/server.mjs:950:52)
    at Module.useAsyncData (file:///workspace/.nuxt/dist/server/server.mjs:974:31)
    at file:///workspace/.nuxt/dist/server/server.mjs:58923:146
    at Module.withAsyncContext (/workspace/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7330:21)
    at setup (file:///workspace/.nuxt/dist/server/server.mjs:58923:95)
    at _sfc_main.setup (file:///workspace/.nuxt/dist/server/server.mjs:58982:23)
    at callWithErrorHandling (/workspace/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:6650:22)

This error raised when request $fetch with option method: 'POST' and has Body: {..}.

after research i found that issue from this
node_modules/ohmyfetch/dist/chunks/fetch.mjs line 122

ctx.options.headers = new Headers(ctx.options.headers);

Headers is undefined, but test same line i see global.Headers available:

console.log('HEADER:', Headers, global.Headers)

HEADER: undefined [class Headers extends URLSearchParams].

Can you help check this issue.
Thank you for wonderful plugin!!

"406 Not Acceptable" with Pocket OAuth API

I am trying to run through an OAuth flow with Pocket. In Step 5, the following code errors with an "406 Not Acceptable":

const { access_token, username } = await $fetch(
  "https://getpocket.com/v3/oauth/authorize",
  {
    method: "POST",
    body: { consumer_key: consumer_key, code: code },
  }
);

Whereas the following fetch command works fine and returns a JSON response:

const res = await fetch("https://getpocket.com/v3/oauth/authorize", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Accept": "application/json",
  },
  body: JSON.stringify({ consumer_key: consumer_key, code: code }),
});

Adjusting the $fetch to

const { access_token, username } = await $fetch(
  "https://getpocket.com/v3/oauth/authorize",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Accept": "application/json",
    },
    body: { consumer_key: consumer_key, code: code },
  }
);

still fails. Any thoughts as to why this may be?

/edit: using v0.4.15

Supporting JSON body

It would be great for POST request to support body as an object.
If this is the case, ohmyfetch will add the correct header and stringify the body.

How to remove 'text/plain;charset=UTF-8' from content-type header?

Hi, I am trying to send a post request to PostgREST to invoke a stored procedure:

  const data = await $fetch('http://http-logger:3001/rpc/test_proc', {
    method: 'post',
    headers: {
      Prefer: 'params=single-object',
      Accept: 'application/json',
      'Content-Type':  'application/json'
    },
    body: {some: 'json'}
})

Results in this request:

POST /rpc/test_proc HTTP/1.1
host: http-logger:3001
connection: keep-alive
prefer: params=single-object
accept: application/json
content-type: application/json, text/plain;charset=UTF-8
accept-language: *
sec-fetch-mode: cors
user-agent: undici
accept-encoding: gzip, deflate
content-length: 15


{"some":"json"}

I need to remove the 'text/plain;charset=UTF-8' part from the content-type. Is this possible? Should it even be there as I have explicitly defined the content-type header?
Many thanks
Ian

Error using $fetch.create using CJS modules.

I was working in a Nuxt2 app using ohmyfetch in the background with a home-made module, but the Nuxt2 app displays me that the$fetch export has not the method create, so I investigate and I found that the CJS module (what it was imported by Webpack after the bundle process) it not exports the method.

Steps to reproduce the error

  1. Create a new JS proyect with the ohmyfetch dependency.
  2. Create a index.js file with the following content.
const { $fetch } = require('ohmyfetch')

const scopedFetch = $fetch.create({});
  1. Try to run the file.

What I was expected

It should not thrown an error.

Error description

const scopedFetch = $fetch.create({});
                           ^

TypeError: $fetch.create is not a function
    at Object.<anonymous> (/home/bantonio/PhpstormProjects/test-ohmyfetch/index.js:3:28)
    at Module._compile (node:internal/modules/cjs/loader:1101:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47

Support Request / Response progress

I know fetch itself didn't have it for a while, last I recall it was coming in 2020 with some command like

allowHTTP1ForStreamingUpload

Do we have any recommendations on what people might want to use for nuxt3?

baseURL is being appended on every retry

Describe the bug
On a request that has both baseURL and retry > 0, baseURL will be appended on every retry attempt.

To Reproduce
Change the code in playground/index.ts to the following:

async function main () {
  // const r = await $fetch<string>('http://google.com/404')
  // const r = await $fetch<string>('http://httpstat.us/500')
  // const r = await $fetch<string>('http://httpstat/500')
  const r = await $fetch<string>('404', { baseURL: 'http://httpstat.us/', retry: 3 }).catch(err => err)
  // eslint-disable-next-line no-console
  console.log(r.request)
}

Then run the following command:

yarn play

Output will show as:

http://httpstat.us/http://httpstat.us/http://httpstat.us/http://httpstat.us/404

Expected behavior
Output should be the following:

http://httpstat.us/404

cancel request

hi this a feature request : how to cancel request? i think this feature would be nice.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.