Search code examples
javascriptaxiosfetchmultipartform-dataform-data

How can I convert this axios call to fetch?


I have the follow axios code which works. I get the expected response back:

(some strings altered for obvious reasons)

const data = new FormData()

data.append('grant_type', 'password')
data.append('username', 'XXXXX')
data.append('password', 'YYYYY')

const url = 'https://www.example.com/endpoint/'

const config = {
  withCredentials: false,
  auth: {
    username: 'AAAAA',
    password: 'BBBBB'
  },
  headers: {
    'Content-Type': 'multipart/form-data'
  }
}

const response = await axios.post(url, data, config)
const answer = response.data.answer
console.log('answer', answer)

I would like to convert this request to fetch, but I am getting a Bad Request message back. I have tried:

const authorization = btoa('AAAAA:BBBBB')

console.log(authorization)

const data = new FormData()

data.append('grant_type', 'password')
data.append('username', 'XXXXX')
data.append('password', 'YYYYY')

console.log(data)

const response = await fetch('https://www.example.com/endpoint/', {
    method: 'POST',
    credentials: 'omit',
    headers: {
      'Content-Type': 'multipart/form-data',
      Authorization: `Basic ${authorization}`
    },
    body: data
})

console.log(response)

Attempting to compare two requests in the Network tab didn't help much. They look nearly identical. The only differences I see in the requests are in the headers:

axios

"accept": "application/json, text/plain, */*",
"content-type": "multipart/form-data; boundary=----WebKitFormBoundaryxYSW9yII1AMmhcc3",

The accept key contains more than just */* and the content-type key contains information on the boundary.

fetch

"accept": "*/*",
"content-type": "multipart/form-data",

I am not sure if the boundary information is important for the content-type nor how to make sure it is included. I tried modifying the code so the accept key would match, but that did not change behavior.


Solution

  • With some additional research, what I learned is that when using fetch to post multipart/form-data, the content-type should not be defined. This allows the system to construct it and add in the required boundary information. Additionally, the credentials setting is not required.

    The fetch request should look like:

    const response = await fetch('https://www.example.com/endpoint/', {
        method: 'POST',
        headers: {
          Authorization: `Basic ${authorization}`
        },
        body: data
    })
    

    With this code change, I get the expected response.

    For documentation on this behavior, see the answer to Tell fetch() to not send a Content-Type header at all