unjs/ofetch

Usage with MSW / patched fetch

meesvandongen opened this issue · 13 comments

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

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?

For anyone coming here, I found a hacky and pretty bad 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

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