reduxjs / redux-toolkit

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

RTK Query - Changing the default header from fetchBaseQuery not working #4669

Open bylly1 opened 1 month ago

bylly1 commented 1 month ago

For almost all endopoints I need the headers.set("Content-Type", "application/json") but there are also some endpoints such as those for uploading files where I need to replace Content-Type", "application/json with Content-Type': 'multipart/form-data. From what I read in other topics, the content-type is set automatically from FormData, but in my case is not working. I also tried to set it manually but still not working


// api.ts
const baseQuery = fetchBaseQuery({
    baseUrl: '/',
    headers: { 'Content-Type': 'application/json'},
    prepareHeaders: (headers, { getState, endpoint }) => {
        // i set this as default for all endpoints
        headers.set("Content-Type", "application/json");

        const token = (getState() as RootState).auth.access;
        if (token && endpoint !== 'refresh') {
            headers.set('Authorization', `Token ${token}`);
        }
        return headers;
    }
});

const baseQueryWithReauth: BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError> = async (
    args,
    api,
    extraOptions
) => {
    let result = await baseQuery(args, api, extraOptions);

    ...rest of the code from reauth
    return result
}
const api = createApi({
    baseQuery: baseQueryWithReauth,
    endpoints: () => ({}),
});

// exampleApi.ts
export const pageApi = api.injectEndpoints({
       endpoints: (builder) => ({
         uploadMediaPage: builder.mutation<
            Partial<ExampleProps>,
            { _id?: string; file: File; type: string;  }
         >({
            query: ({ _id, ...formData }) => {
                let bodyFormData = new FormData();
                bodyFormData.append('file', formData.file);
                bodyFormData.append('type', formData.type);

                return {
                    url: `http://localhost:5000/${_id}/upload/`,
                    method: 'POST',
                    body: bodyFormData,

                    // I tried to set manually but is not overwriting the one from api.ts
                    headers: {
                         'Content-Type': 'multipart/form-data',
                    },
                };
            },
        })
})
phryneas commented 1 month ago

I think your prepareHeaders overrides the headers from the endpoint again. Skip that part and see if that already does the trick.

Priority here is

bylly1 commented 1 month ago

I think your prepareHeaders overrides the headers from the endpoint again. Skip that part and see if that already does the trick.

Priority here is

  • (low) headers from fetchBaseQuery
  • (med) headers from endpoint query
  • (high) prepareHeaders

the problem is that i cannot set a default header. I tried to add Content-Type, "application/json to fetchBaseQuery. I was thinking that If I set Content-Type: 'multipart/form-data to query will override fetchBaseQuery header but not. The current solution is to remove Content-Type, "application/json from fetchBaseQuery and also Content-Type: 'multipart/form-data from query because this will be set automatically by the FormData from the query.

// approch 1 - set default header to fetchBaseQuery and custom header to query --  not working

const baseQuery = fetchBaseQuery({
    headers: { 'Content-Type': 'application/json'},              // low priority
    ....rest code
});
export const pageApi = api.injectEndpoints({
       endpoints: (builder) => ({
         uploadMediaPage: builder.mutation<
            Partial<ExampleProps>,
            { _id?: string; file: File; type: string;  }
         >({
            query: ({ _id, ...formData }) => {
                let bodyFormData = new FormData();
                bodyFormData.append('file', formData.file);
                bodyFormData.append('type', formData.type);

                return {
                    url: `http://localhost:5000/${_id}/upload/`,
                    method: 'POST',
                    body: bodyFormData,
                    headers: {
                         'Content-Type': 'multipart/form-data',   // med priority
                    },
                };
            },
        })
})

 // approch 2 - remove custom header from query (set automatically) and set default to fetchBaseQuery -- not working

const baseQuery = fetchBaseQuery({
    headers: { 'Content-Type': 'application/json'},              // low priority
    ...rest of the code
});
export const pageApi = api.injectEndpoints({
       endpoints: (builder) => ({
         uploadMediaPage: builder.mutation<
            Partial<ExampleProps>,
            { _id?: string; file: File; type: string;  }
         >({
            query: ({ _id, ...formData }) => {
                let bodyFormData = new FormData();
                bodyFormData.append('file', formData.file);
                bodyFormData.append('type', formData.type);

                return {
                    url: `http://localhost:5000/${_id}/upload/`,
                    method: 'POST',
                    body: bodyFormData,
                    headers: {
                         'Content-Type': 'multipart/form-data',   // med priority
                    },
                };
            },
        })
})

approch 3 -- working but I need to remove the default header for all endpoints

const baseQuery = fetchBaseQuery({
     // headers: { 'Content-Type': 'application/json'},              // I need to remove the default header
    ...rest of the code
});
export const pageApi = api.injectEndpoints({
       endpoints: (builder) => ({
         uploadMediaPage: builder.mutation<
            Partial<ExampleProps>,
            { _id?: string; file: File; type: string;  }
         >({
            query: ({ _id, ...formData }) => {
                let bodyFormData = new FormData();
                bodyFormData.append('file', formData.file);
                bodyFormData.append('type', formData.type);

                return {
                    url: `http://localhost:5000/${_id}/upload/`,
                    method: 'POST',
                    body: bodyFormData
                };
            },
        })
})
phryneas commented 1 month ago

Hmm, might be a fetch quirk that you're not allowed to set a header for formdata to kick in :/ Did you try setting it to undefined instead of 'multipart/form-data'?

bylly1 commented 1 month ago

I asked chat gpt and he gave me a solution that works, but I don't know if is a good approch:


const baseQuery = fetchBaseQuery({

    prepareHeaders: (headers, { getState, endpoint, arg }) => {
        if (arg && typeof arg === 'object' && 'body' in arg && arg.body instanceof FormData) {
            // Remove Content-Type for FormData to let the browser set it
            headers.delete('Content-Type');
        } else {
            // Set Content-Type to application/json for all other requests
            headers.set('Content-Type', 'application/json');
        }

        return headers;
    },
});

Inside query I let browser to automatically set the header content-type multipart/form-data

bylly1 commented 1 month ago

Hmm, might be a fetch quirk that you're not allowed to set a header for formdata to kick in :/ Did you try setting it to undefined instead of 'multipart/form-data'?

Setting Content-Type: undefined inside query and headers: { 'Content-Type', 'application/json' } to fetchBaseQuery also works