reduxjs / redux-toolkit

The official, opinionated, batteries-included toolset for efficient Redux development
https://redux-toolkit.js.org
MIT License
10.72k stars 1.17k forks source link

TypeError: Invalid URL: /api for RTK Query using relative path during testing #3284

Closed bogdan-pechounov closed 1 year ago

bogdan-pechounov commented 1 year ago

Since I have an NGINX controller routing requests to either the api or the react client, both the api and the client have the same base url. Therefore, I can make requests to /api/auth/me instead of http://localhost/api/auth/me. authApi.ts

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { User, UserLogin } from '../../types/user'

// Define a service using a base URL and expected endpoints
export const authApi = createApi({
  reducerPath: 'authApi',
  baseQuery: fetchBaseQuery({ baseUrl: '/api/auth' }), # !!!
  endpoints: (builder) => ({
    me: builder.query<User, void>({
      query: () => ({ url: '/me' }),
    }),
    //Login
    login: builder.mutation<User, UserLogin>({
      query: (user) => ({
        url: '/login',
        method: 'POST',
        body: user,
      }),
    }),
    //Logout
    logout: builder.mutation<User, void>({
      query: () => ({
        url: '/logout',
        method: 'POST',
      }),
    }),
    //Sign up
    signup: builder.mutation<User, UserLogin>({
      query: (user) => ({
        url: '/signup',
        method: 'POST',
        body: user,
      }),
    }),
  }),
})

// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const {
  useMeQuery,
  useLoginMutation,
  useSignupMutation,
  useLogoutMutation,
} = authApi

There are no issues when starting a server, but when I run tests with msw mocking api calls, I get an error.

stderr | src/App.test.tsx > App > renders
An unhandled error occurred processing a request for the endpoint "me".
In the case of an unhandled error, no tags will be "provided" or "invalidated". TypeError: Failed to parse URL from /api/auth/me
    at new Request (node:internal/deps/undici/undici:9474:19)
    at Proxy.<anonymous> (C:\Users\bpech\Me\Learning\Projects\microservices-quiz-app\react-client\node_modules\@reduxjs\toolkit\src\query\fetchBaseQuery.ts:267:21)
    ... 2 lines matching cause stack trace ...
    at fulfilled (C:\Users\bpech\Me\Learning\Projects\microservices-quiz-app\react-client\node_modules\@reduxjs\toolkit\dist\query\rtk-query.cjs.development.js:95:32) {
  [cause]: TypeError: Invalid URL: /api/auth/me
      at new URLImpl (C:\Users\bpech\Me\Learning\Projects\microservices-quiz-app\react-client\node_modules\whatwg-url\lib\URL-impl.js:21:13)
      at Object.exports.setup (C:\Users\bpech\Me\Learning\Projects\microservices-quiz-app\react-client\node_modules\whatwg-url\lib\URL.js:54:12)     
      at new URL (C:\Users\bpech\Me\Learning\Projects\microservices-quiz-app\react-client\node_modules\whatwg-url\lib\URL.js:115:22)
      at new Request (node:internal/deps/undici/undici:9472:25)
      at Proxy.<anonymous> (C:\Users\bpech\Me\Learning\Projects\microservices-quiz-app\react-client\node_modules\@reduxjs\toolkit\src\query\fetchBaseQuery.ts:267:21)
      at step (C:\Users\bpech\Me\Learning\Projects\microservices-quiz-app\react-client\node_modules\@reduxjs\toolkit\dist\query\rtk-query.cjs.development.js:23:23)
      at Object.next (C:\Users\bpech\Me\Learning\Projects\microservices-quiz-app\react-client\node_modules\@reduxjs\toolkit\dist\query\rtk-query.cjs.development.js:4:53)
      at fulfilled (C:\Users\bpech\Me\Learning\Projects\microservices-quiz-app\react-client\node_modules\@reduxjs\toolkit\dist\query\rtk-query.cjs.development.js:95:32)
}

I am not sure why it happens since even the baseURI is faked: console.log(document.baseURI) prints http://localhost:3000/. If I use http://localhost/api/auth instead of /api/auth, there are no errors, but I would prefer avoiding setting the baseUrl for production and development if possible.

repo: https://github.com/bogdan-pechounov/microservices-quiz-app/tree/8983e6456e00967896adbdb30b47f6af7be1bd08 directory: react-client commands: npm install npm test

(To run it, use the skaffold.yaml file to start the apis as well. skaffold run -f skaffold.dev.yaml)

phryneas commented 1 year ago

In node, there is no domain. You cannot run any relative requests from node - you will always need a full url for your tests if they don't run in a browser.

bogdan-pechounov commented 1 year ago

I see. I thought it could be faked by setting a field in document, but it seems to be more complicated than this. example

Edit: I found in the msw documentation that relative paths are supported.

// Given your application runs on "http://localhost:8080",
// this request handler URL gets resolved to "http://localhost:8080/invoices"
rest.get('/invoices', invoicesResolver)
Note that relative URL are resolved against the current location (location.origin).

If I console.log(window.location.origin), it also prints http://localhost:3000. I tried using fetch and it throws the same error TypeError: Failed to parse URL from /api/test, so it's not an issue with RTK Query.

abbas-dhd commented 1 year ago

@bogdan-pechounov I'm having same issue, were you able to resolve it?

bogdan-pechounov commented 1 year ago

@abbas-dhd The base query needs to be a full path baseQuery: fetchBaseQuery({ baseUrl: window.location.origin + '/api/auth' }). The mock handler can still use a relative path.

rest.get('/api/auth/me', (req, res, ctx) => {
    console.log('REQUEST')
    return res(ctx.status(200), ctx.json({ username: 'User' }))
  }),
kettanaito commented 11 months ago

One comment on my side: @phryneas is completely right—there are no relative URLs in Node.js, nothing to be relative to! MSW doesn't claim it supports it either. What MSW tells you is that if you are using browser-like environments in Node.js (like JSDOM), those environments will polyfill the global location and then MSW will be able to resolve relative URLs against that location similar to how it does so in the browser.

abbas-dhd commented 11 months ago

I solved it by setting base URL in .env files. and had a mock URL in .env.test something like http://www.example.com. since I was using MSW only while running unit tests it would take full mock URL.

kettanaito commented 7 months ago

I've updated MSW docs to include two things:

I hope this helps folks get to the root cause and the fix faster now.