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 - Changing the default header from fetchBaseQuery not working #4669

Open
bylly1 opened this issue Oct 16, 2024 · 5 comments
Open

Comments

@bylly1
Copy link

bylly1 commented Oct 16, 2024

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
Copy link
Member

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

@bylly1
Copy link
Author

bylly1 commented Oct 16, 2024

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
Copy link
Member

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
Copy link
Author

bylly1 commented Oct 17, 2024

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
Copy link
Author

bylly1 commented Oct 17, 2024

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

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

2 participants