unjs / ofetch

😱 A better fetch API. Works on node, browser and workers.
MIT License
4.15k stars 127 forks source link

Usage with MSW / patched fetch #295

Open meesvandongen opened 1 year ago

meesvandongen commented 1 year ago

Environment

Node

Reproduction

https://codesandbox.io/p/sandbox/stoic-https-6gjdtz?file=/index.js:14,16

import { setupServer } from "msw/node";
import { http, HttpResponse } from "msw";
import wretch from 'wretch'
import { ofetch, fetch, createFetch } from 'ofetch'
import got from 'got'

const server = setupServer(
    http.get('https://example.com', () => {
        return HttpResponse.text('1')
    })
)
server.listen();

console.table([
 ['fetch', await globalThis.fetch('https://example.com').then(res => res.text()).then(res => res === '1')],
 ['ofetch fetch', await fetch('https://example.com').then(res => res.text()).then(res => res === '1')],
 ['ofetch createFetch', await createFetch()('https://example.com').then(res => res === '1')],
 ['ofetch', await ofetch('https://example.com').then(res => res === '1')],
 ['wretch', await wretch('https://example.com').get().text().then(res => res === '1')],
 ['got', await got.get('https://example.com').text().then(res => res === '1')],
])

CleanShot 2023-12-23 at 14 00 06@2x

Describe the bug

When other tools patch fetch, the global instance of ofetch will have the original fetch instance. This will cause tools such as msw to not be able to intercept requests with ofetch. A workaround could be to only get the globalThis.fetch at request time if no other fetch is specified.

Additional context

Users can work around this at this time by using the createFetch helper function.

Logs

No response

mohammadGh commented 1 year ago

Thanks @meesvandongen for opening this issue, which is also a problem we have encountered. PooyaJaan @pi0 please let us know what is the best and efficient temporary solution to solve this problem until it is resolved? Should we use the createFetch function?

edumudu commented 12 months ago

For anyone coming here, I found a hacky but functional way to workaround this for now using the Proxy constructor

function createMyFetch() {
  const newFetch = createFetch({
    fetch: globalThis.fetch,
    Headers: globalThis.Headers,
  });

  return newFetch.create({
    // ...default options
  });
}

const original = createMyFetch();

export const http = new Proxy(original, {
  apply(_, thisArg, argumentsList) {
    return Reflect.apply(createMyFetch(), thisArg, argumentsList);
  },
});

// Then in other file calling `http` will be intercepted but maintaining the same usage
http('https://jsonplaceholder.typicode.com/todos/1')

This will work with msw@2.0.8 and node@18.18.0. You can merge the arguments instead of creating a new stance every time, or creating one only on the first call, etc

rinux55 commented 11 months ago

Running into this issue as well, a fix or workaround for usage with msw would be greatly appreciated.

stichingsd-vitrion commented 10 months ago

Running into this issue as well, a fix or workaround for usage with msw would be greatly appreciated.

Same here, not working with Nuxt in my case for MSW serverside outgoing calls EDIT:

After more debuggin in nuxtJS i found out above issue is main issue mocking in server/api is not working (calls to third parties

export default defineEventHandler(async (event) => {
  const data = await createFetch()(
    'https://jsonplaceholder.typicode.com/todos/1',
    {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      },
    },
  )
  console.log(JSON.stringify(data))
  return {
    hello: 'world',
  }
})

Right now i fixed it by using the createFetch insteda of $fetch from nuxt.

mydea commented 9 months ago

Just adding on here, this is also apparently a problem when using Sentry in the Browser - any requests made via ofetch will not be captured by the monkey-patched fetch that Sentry relies on to capture network requests, sadly.

dwightjack commented 8 months ago

I think this same problem (even if not strictly an issue with ofetch) happens with Datadog RUM: https://github.com/unjs/ofetch/issues/339

dwightjack commented 8 months ago

I might have found a possible, non-breaking solution to this problem and submitted a PR, but I wonder whether it is effective in all scenarios.

mentalrob commented 7 months ago

Any update on this ?

cy-moi commented 6 months ago

Here is a possible workaround for me with useFetch hook in Nuxt.

useFetch(url, {
    $fetch: createFetch({
      fetch: globalThis.fetch,
      Headers: globalThis.Headers,
    }), 
   ...options,
  });

Maybe this could be better supported in Nuxt.

artmizu commented 6 months ago

I'm too humbly awaiting the solution, thank you, guys, for your time! Tried to overcome the issue by myself, but for now don't see any available options.

shunnNet commented 2 months ago

For someone who stuck in Nuxt + ofetch, I have created a Nuxt module nuxt-msw, which integrate msw with Nuxt.

cctidal commented 2 months ago

This issue was a deciding factor against using ofetch when migrating an app that was using request even though the ofetch api is really nice :disappointed:

pi0 commented 2 months ago

Next version of ofetch will use patched fetch.