suhaotian / xior

A lite request lib based on fetch with plugin support and similar API to axios.
https://suhaotian.github.io/xior/
MIT License
193 stars 4 forks source link

transformRequest method is missing #21

Closed JustDenP closed 4 months ago

JustDenP commented 4 months ago

Hello! First of all, thank you for the perfect fetch library, but the only thing missing for me is transformRequest method from Axios to modify per-request options.

For example, if I need to completely remove some headers from specific requests, like Content-Type. In axios we can do it like so:

axiosInstance.post('/endpoint', { ... }, {
  transformRequest: [
    (data, headers) => {
      delete headers.common['Content-Type'];
      return JSON.stringify(data);
    },
  ],
});

Instead of creating separate clients or modifying defaults. https://stackoverflow.com/questions/46656474/axios-remove-headers-authorization-in-1-call-only

suhaotian commented 4 months ago

Hey for the case of https://stackoverflow.com/questions/46656474/axios-remove-headers-authorization-in-1-call-only

We can override the value if use the defaults config:

xiorInstance.post('/endpoint', {...}, {
  headers: {
     'Authorization': '',
  }
})

For your case, I'm curious why you want delete the Content-Type in headers?

Because the default behaviour in xior.js, it will automatic process the dataJSON.stringify(data) and add Content-Type: 'application/json' in POST method:

axiosInstance.post('/endpoint', { ... }, {
  transformRequest: [
    (data, headers) => {
      delete headers.common['Content-Type'];
      return JSON.stringify(data);
    },
  ],
});

Same with:

xiorInstance.post('/endpoint', { ... }); 
fetch('/endpoint', {
  method: 'post', body: JSON.stringify({...}), headers: {'Content-Type': 'application/json'} 
})

For the feature transformRequest or transformResponse, May will consider to support in next version. Thanks!

JustDenP commented 4 months ago

Thanks for your answer, but there are cases when you want to remove the whole header, for example this one: https://stackoverflow.com/questions/39280438/fetch-missing-boundary-in-multipart-form-data-post https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data

I use Xior also as a wrapper for Orval SDK generator, which sets some options, and headers automatically by provided openspec and with Content-Type multipart/form-data, I'm getting errors about missing Boundary. The only way I found to fix it - simply remove the Content-Type header and now it works. "Content-Type: undefined" didn't help because of "Unsupported Media Type" error from server.

The problem is I disabled the 'Content-Type': 'application/json' from my base Xior instance and I cannot use my SDK function, because I cannot override it. With something like transformRequest or transformResponse it would be super easy!

JustDenP commented 4 months ago

Also I'm not sure about "Because the default behaviour in xior.js, it will automatic process the dataJSON.stringify(data) and add Content-Type: 'application/json' in POST method", because if it's NOT set in default like this

export const ApiClient = xior.create({
    baseURL,
    headers: {
        'Content-Type': 'application/json'
    },
    credentials: 'include'
});

Then post request will not have it, and it fixed my problem

CleanShot 2024-06-30 at 11 10 02@2x

suhaotian commented 4 months ago

To remove headers for specify endpoint, we can use request interceptors to modify request object:

xiorInstance.interceptors.request.use((config) => {
  if (config.url === '/endpoint') {
     delete config.headers['Content-Type']
  }
  /* 
    or delete the `Content-Type` if the data is FormData, 
    because if it's formData, browser will automatically add headers 
  */
  // if (config.data instanceof FormData) {
  //   delete config.headers['Content-Type']
  // }
  return config;
});

BTW, The 'Content-Type': 'application/json' is no necessary:

export const ApiClient = xior.create({
    baseURL,
    headers: {
        'Content-Type': 'application/json'
    },
    credentials: 'include'
});

Can just be:

export const ApiClient = xior.create({
    baseURL,
    credentials: 'include'
});

And upload file example:

const formData = FormData();
formData.append('file', fileObject);
formData.append('field1', 'val1');
formData.append('field2', 'val2');

xiorInstance.post('/upload', formData);