redux-toolkit: RTKQ: fetchFn using cross-fetch fails

When using fetchFn from createApi, isomorphic-fetch works but cross-fetch fails.

Both seem to run fine in react without RTKQ. But with cross-fetch+RTKQ, a request that should be: /some-endpoint is run in the browser as /[object%20Request].

Possible I’m an idiot and flubbing the cross-fetch import and that’s the problem, but I’ve tried a few different import/require default/destructured variations and none seem to solve the issue.

...
const { fetch } = require("cross-fetch");  // doesn't work
// const fetch = require("isomorphic-fetch");  // works

// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
  reducerPath: "pokemonApi",
  baseQuery: fetchBaseQuery({
    baseUrl: "https://pokeapi.co/api/v2/",
    fetchFn: fetch,
  }),
...

Minimal Codesandbox example

https://codesandbox.io/s/vibrant-resonance-cvf7m?file=/src/services/pokemon.ts

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 15 (5 by maintainers)

Commits related to this issue

Most upvoted comments

Moved import 'whatwg-fetch'; to the very first line of Jest’s setupTests.ts file and the warning was gone 🥳 Thanks!

import 'whatwg-fetch'; perfectly solve the problem in node test env using msw

The following works fine for me:

jest.setup.js

import AbortController from 'abort-controller';
import { fetch, Headers, Request, Response } from 'cross-fetch';
import { server } from './src/server';

global.fetch = fetch;
global.Headers = Headers;
global.Request = Request;
global.Response = Response;
global.AbortController = AbortController;

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

server.js

import { setupServer } from 'msw/node';
import { authApiMock } from '@/services/auth/authApiMock';
import { eventsApiMock } from '@/services/events/eventsApiMock';

export const server = setupServer(...authApiMock, ...eventsApiMock);

import 'whatwg-fetch'; perfectly solve the problem in node test env using msw

Wow. 3 hours of my life and that was the solution. Thank you. Testing the frontend is the hardest part of software development. 1 day to build the feature. 1 week if you try to write tests.

Had the same issue and adding

import 'isomorphic-fetch';

into setup file helped for me

It matters when you do that. If fetchBaseQuery is executed before 'whatwg-fetch' is included, you will get that message. It will work anyways, since it tries to access the global fetch later, but technically it is not available in the beginning.

So import order plays a role there

@donatoaguirre24 That is a super slick test setup. I was passing isomorphic-fetch to fetchFn and making actual API calls, but this is 100X better. I avoid needing cross-fetch in my bundle and my tests are fast thanks to msw.

Thanks for all the help @phryneas @msutkowski @donatoaguirre24, and to the team for RTK. RTK is so good I feel guilty using it, esp now w/ RTKQ and its open API codegen.

Ah… I think I remember something from the old repo: https://github.com/rtk-incubator/rtk-query/issues/88#issuecomment-743797233

It seems that cross-fetch does not set the Request on global.

Doing it by hand fixes that:

import fetch, { Headers, Request, Response } from 'node-fetch';
import AbortController from 'abort-controller';

global.fetch = fetch as any;
global.Headers = Headers as any;
global.Request = Request as any;
global.Response = Response as any;
global.AbortController = AbortController;

The weird thing is that even node-fetch seems to mention Request: node-fetch/node-fetch#new-requestinput-options

But the source code I linked above seems to indicate otherwise… so I honestly don’t really know what’s going on there.

That’s weird - they both worked when I was testing all of these things - we even used cross-fetch in a test. I can investigate if necessary.

@RileyMShea Regarding testing with jest, feel free to look at the test setup for the repo. Jest runs in a node environment, so you’re going to have to polyfill… we use node-fetch:

//@ts-ignore
const nodeFetch = require('node-fetch')
//@ts-ignore
global.fetch = nodeFetch
//@ts-ignore
global.Request = nodeFetch.Request
const { server } = require('./src/query/tests/mocks/server')

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

process.on('unhandledRejection', (error) => {
  // eslint-disable-next-line no-undef
  fail(error)
})

import 'whatwg-fetch'; perfectly solve the problem in node test env using msw

This was the only way how I got my tests running. Thanks for that 👍 (TestingLibrary+MSW+RTKQ+Jest).

But I am still seeing the warning that fetch is not available:

console.warn
    Warning: `fetch` is not available. Please supply a custom `fetchFn` property to use `fetchBaseQuery` on SSR environments.

Which is really puzzling, is this really the way to get fetchBaseQuery to “work” in a Jest environment?

@RileyMShea Pro tip: Just add abort-controller and cross-fetch as dev dependencies so they are not bundled in the production code

fetchBaseQuery is doing

    const request = new Request(url, config)
    const response = await fetchFn(request)

cross-fetch is using node-fetch and that just does not support Request argument signature fetch(request), they just support fetch(url, [init]).

https://github.com/node-fetch/node-fetch/blob/ffef5e3c2322e8493dd75120b1123b01b106ab23/src/index.js#L34-L37