Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RTK Query - Setting headers doesn't work #2205

Closed
mohamedamara1 opened this issue Apr 5, 2022 · 27 comments
Closed

RTK Query - Setting headers doesn't work #2205

mohamedamara1 opened this issue Apr 5, 2022 · 27 comments

Comments

@mohamedamara1
Copy link

mohamedamara1 commented Apr 5, 2022

I'm trying to send a POST request and I need to set the 'content-type' header to 'application/json', I'm using fetchBaseQuery and it's supposed to automatically do that but it's not working because when I check the sent request using Chrome Dev Tools and checking the request headers I don't see 'content-type' set to 'application/json'. I don't see it at all so I tried to manually do that by either using prepareHeaders like this

// Or from '@reduxjs/toolkit/query' if not using the auto-generated hooks
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

// initialize an empty api service that we'll inject endpoints into later as needed
export const mainApi = createApi({
  reducerPath: "mainApi",
  baseQuery: fetchBaseQuery({
    baseUrl: "http://localhost:5000/api/v1/",
    prepareHeaders: (headers) => {
      headers.set("Content-Type", "application/json");
      console.log(headers);
      return headers;
    },
  }),
  endpoints: () => ({}),
});

it still won't work, btw console.log(headers) only returns an empty Headers object .
I also tried to set the header like this

    addUser: builder.mutation({
      query: (data) => {
        //  console.log(typeof body);
        return {
          url: `auth/signup`,
          method: "POST",
          headers: { "content-type": "application/json" },
          body: JSON.stringify(data),
        };
      },
    }),

I still can't see the 'content-type' header when checking it using browser dev tools.

image

@phryneas
Copy link
Member

phryneas commented Apr 5, 2022

fetchBaseQuery will only set a content-type header on an object. You don't need to stringify that yourself.

    addUser: builder.mutation({
      query: (data) => {
        return {
          url: `auth/signup`,
          method: "POST",
          body: data
        };
      },
    }),

should be perfectly fine, assuming you put a normal JavaScript object and not some class instance in there.

As for why your console.log(headers) does not log content-type, I cannot tell you: that is a completely normal Headers object, created with a new Headers(...) call. Did you maybe load some faulty polyfills somewhere in your application?

@mohamedamara1
Copy link
Author

Passing the body object like that still won't work too, I was passing it like that at first, what I'm trying to achieve is sending a POST request for registering a user and I'm using supertokens library for auth and I had an issue with that so after searching they said I need to set the 'content-type' of my request to 'application/json' , this is what I'm trying to achieve right now with RTK Query.

This is how I'm creating the body object

const entries = Object.entries(row).map(([key, value]) => {
        return {
          id: key,
          value,
        };
      });
      entries.push({ id: "role", value: "teacher" });
      entries.push({ id: "password", value: "pass1234" });
      entries.push({ id: "address", value: "" });

      console.log(entries);
      const body = {
        formFields: entries,
      };
      addTeacher(body);

It still won't work, I have also tried deleting the npm_modules folder and reinstalling everything but still the same issue.

@phryneas
Copy link
Member

phryneas commented Apr 5, 2022

Well, all I can say is that this should work. You can verify it in https://codesandbox.io/s/github/reduxjs/redux-toolkit/tree/master/examples/query/react/authentication-with-extrareducers?from-embed=&file=/src/app/services/auth.ts for example. We don't do anything different there.

My only other idea would be that you have CORS problems, but then you should have very big red warnings on your browser console about that.

@mohamedamara1
Copy link
Author

This is very odd but I'll keep digging, thanks anyway!

@anmilleriii
Copy link

I am also having a very similar issue with SuperTokens + RTK Query (Content-Type header is not being applied with fetchBaseQuery using prepareHeaders, assigning object literal to headers, or assigning the header within the header key in an endpoint's query.

I think the problem lies with the way SuperTokens interacts w/fetchBaseQuery (see https://github.com/supertokens/supertokens-website/blob/4f2399579b27bf29a39602170df4b0be9a3c7c7b/lib/build/recipeImplementation.js#L163)

    return {
        addFetchInterceptorsAndReturnModifiedFetch: function(originalFetch, _) {
            return function(url, config) {
                return __awaiter(this, void 0, void 0, function() {
                    return __generator(this, function(_a) {
                        switch (_a.label) {
                            case 0:
                                return [
                                    4 /*yield*/,
                                    fetch_1.default.doRequest(
                                        function(config) {
                                            **return originalFetch(url, __assign({}, config));** // Failed to load resource: the server responded with a status of 400 (Bad Request) here
                                        },
                                        config,
                                        url
                                    )
                                ];
                            case 1:
                                return [2 /*return*/, _a.sent()];
                        }
                    });
                });
            };
        },

A trivial/suboptimal workaround for right now is to circumvent fetchBaseQuery with a queryFn:

async queryFn({ email, password }) {
        const body = {
          formFields: [
            {
              id: "email",
              value: email,
            },
            {
              id: "password",
              value: password,
            },
          ] as FormFields,
        };
        const result = await fetch(`${BASE_URL}/signin`, {
          method: "POST",
          headers: { rid: "emailpassword", "Content-Type": "application/json" },
          body: JSON.stringify(body),
        });
        const data = await result.json();
        return { data: data };
      },

@phryneas
Copy link
Member

@anmilleriii that looks like that "modified fetch" just does not support getting passed a Request object as the first argument.

The first argument to fetch has to be a RequestInfo which is either a string, or a Request class instance.

Supertokens fails to implement the standard there.

@anmilleriii
Copy link

Thanks for the quick response @phryneas!

I think that your comment here correctly identified this issue (and clarified that it pertains to SuperTokens, not RTK Query).

Therefore I would consider this issue out of scope to RTK query and closable here, (fixing the SuperTokens implementation of their fetch wrapper is now covered by this issue.

@lnicepei
Copy link

lnicepei commented Jan 31, 2023

I have a similar problem to @mohamedamara1's.

export const loginApi = createApi({
  reducerPath: "loginApi",
  baseQuery: fetchBaseQuery({
    prepareHeaders: (headers) => {
      headers.set("Content-Type", "application/json");
      headers.set("accept", "text/plain");
      return headers;
    },
    mode: "no-cors",
    baseUrl: process.env.REACT_APP_MOBILE_DEV_BASE_URL
  }),
  endpoints: (builder) => ({
    login: builder.mutation<UserResponse, LoginRequest>({
      query: (credentials) => ({
        url: "login",
        method: "POST",
        body: credentials
      })
    })
  })
});

The requested format of Content-Type on the server is application/json. But the code does not set the appropriate value for Content-Type header, and leaves text/plain;charset=UTF-8 instead(throwing error 415: unsupported media type). What could I change to set the needed Content-Type: application/json for the request header?

@phryneas
Copy link
Member

@lnicepei are you using fetchBaseQuery or a custom baseQuery?

Generally: the only content-type that RTKQ+fetchBaseQuery would ever set is 'application/json'. Are you maybe looking at the response header, not the request header, here?

@lnicepei
Copy link

lnicepei commented Jan 31, 2023

I'm using fetchBaseQuery, and here are the request headers
image

@lnicepei
Copy link

BTW, when I remove mode: "no-cors" from fetchBaseQuery, I see this(content-type: application/json suddenly appears):

image

But the request fails:

image

@phryneas
Copy link
Member

In that case, this is browser behaviour and has nothing to do with RTK Query. RTK Query just calls fetch - and you would probably observe the same behaviour if you would call fetch manually by yourself. I'd suggest you try that to get to a point where the normal fetch call works and then go back into using RTK Query here.

Generally: CORS problems like this have to be solved at the server side - the server has to send headers that indicate that a requerst of this kind is allowed.

@lnicepei
Copy link

Thank you so much(!)
It was the CORS problem on the backend. My http://localhost:3000 was not in the list of allowed resources

@dmccoy-NL
Copy link

dmccoy-NL commented Feb 28, 2023

Hello, having a smiliar issue with POST/PATCH/PUT. However this is not caused by any cors settings (have tested). Using the rtk code gen @rtk-query/codegen-openapi and with my empty api set up as below, I can clearly see the prepareHeaders function is called properly (although it is an empty map), however the header is never actually set.

  baseQuery: fetchBaseQuery({
    baseUrl: API_BASE_URL,
    paramsSerializer: (params) => {
      return toMetaParams(params as IMeta, true);
    },
    prepareHeaders: (headers) => {
      headers.set('Content-Type', 'application/json');
      console.log(headers);
      return headers;
    }
  }),
  endpoints: () => ({})
});

@frank-mendez
Copy link

I have similar problem with most of you guys, I solved it by enabling the server CORS, by default fetchBaseQuery has Content-Type: 'application/json' so this issue is on the server

@dmccoy-NL
Copy link

dmccoy-NL commented Mar 10, 2023

Maybe I missed something, but the request doesn't have that header at all, all the requests were working fine with axios and fetch (get). Also, cors is enabled on the server already.

@lucasgarfield
Copy link

lucasgarfield commented Mar 22, 2023

@dmccoy-NL I have been struggling with the same issue all morning. Like you, my fetch (post) and axios requests are working fine but fetchBaseQuery post requests are missing the Content-Type header.

Like you, I also tried setting the header myself from the prepareHeaders callback - it did not work.

Have you had any luck?

(I had to edit this comment, I had thought I had gotten it working but was incorrect... my code was still calling plain old fetch() in the working version...)

@phryneas
Copy link
Member

If any of those can create a reproduction (keep in mind, we need a client and server for this!) we can give feedback.
But like this it's impossible to say anything about this.
You could all have the same problem, you could have completely different problems that just manifest in the same way 🤷‍♂️

@daniyalayyaz
Copy link

fetchBaseQuery

Even in this example, headers are not being set. Please have a look in browser network tab and try to find the authorization header

@lucaspieran
Copy link

hi, could someone solve it? I'm stuck with this when sending a header Authorization sends it to me in lowercase

@phryneas
Copy link
Member

phryneas commented May 2, 2023

@lucaspieran HTTP headers are per standard case-insensitive. HTTP2 even enforces them to be lowercase.

@lucaspieran
Copy link

@phryneas oh i see, that is to say that the lowercase cannot be solved?

@phryneas
Copy link
Member

phryneas commented May 4, 2023

Headers being lowercase is a part of the standard. There's nothing to be solved, they should always be compared case-insensitive and best treated lowercase. If you expect them to be non-lowercase on the server, you go against HTTP standards.

Also, it's not RTKQ that makes them lowercase. Your browser is.

@George-field
Copy link

I too am having this issue, not sure why the headers are not setting. I am using NextJS.

 createUser: builder.query({
      query: () => `/users`,
    }),

This function is running and it's fetching the query token but it's not appearing on the server. I checked the request headers and its missing from it, there are many other headers there.

 baseQuery: fetchBaseQuery({
    baseUrl: process.env.API_URL as string,
    prepareHeaders: (headers) => {
      // get auth token and append headers
      auth.currentUser?.getIdToken().then(token => {
        console.log(token, 'token')
        return headers.set('x-id-token', token)
      })

      return headers;
    }
  }),

@phryneas
Copy link
Member

phryneas commented May 9, 2023

99% it's CORS. Does the server declare to the browser that the browser is allowed to send these headers? Do you see an error message?

@George-field
Copy link

George-field commented May 9, 2023

99% it's CORS. Does the server declare to the browser that the browser is allowed to send these headers? Do you see an error message?

I checked all of the cors issues, it wasn't that. I have fixed it though, turns out it is my own code.

Basically, I changed the prepareHeaders to an async function. The function was returning the value before the promise had been resolved.

 prepareHeaders: async (headers) => {
      // get auth token and append headers
      const token = await auth.currentUser?.getIdToken()
      headers.set('x-id-token', token as string)
   
      return headers
    }

Thank you for your help! :)

@lucasgarfield
Copy link

Posting an update here... after struggling back in March, I've found the solution for my specific case.

I work on a micro frontend, and it turns out that the framework it runs in implemented a wrapper around fetch that was implemented incorrectly.

As phryneas notes above,

The first argument to fetch has to be a RequestInfo which is either a string, or a Request class instance.

In our case, the fetch wrapper was overwriting any headers in the Request class instance that RTK Query passes to fetch. It was very similar to the issue caused by the supertokens library discussed earlier in this thread.

To anyone else experiencing this issue in the future... as suggested by phryneas, check for a fetch wrapper/polyfill! It is easy to implement this incorrectly such that the headers get dropped/overwritten!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests