node.jscurlfetch-apihttp-content-lengthisomorphic-fetch-api

How to POST with multipart/form-data header and FormData using fetch


This is a CURL example which works fine:

curl -X POST \
  <url> \
  -H 'authorization: Bearer <token>' \
  -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
  -F file=@algorithm.jpg \
  -F userId=<userId>

I'm trying to reproduce this request using isomorphic-fetch.

I've tried the following code:

const formData = new FormData();
formData.append('file', file);
formData.append('userId', userId);

return fetch(`<url>`, {      
  method: 'POST',
  headers: {
    'Content-Length': file.length
    'Authorization: Bearer <authorization token>',
    'Content-Type': 'multipart/form-data'
  },
  body: formData
})`

I use fs.readFileSync in order to generate the file passed to FormData.

The previous example returns a 401 HTTP status code (unauthorized) with an error message saying that the userId embedded in the token (sent via header) does not match the userId passed from formData.

So my suspicion is that the FormData that arrives to the REST API is not adequately formed.

The problem may be related with the Content-Length header, but I didn't find a better way to calculate it (if I don't use the Content-Length header I get a 411 HTTP status code Content-Length header missing).

Could be the case that this is failing because of an incorrect value in the Content-Length header?

Any other suggestions on why this is failing or how to better debug it?

If further info is needed to clarify this problem, please just ask.

UPDATE

I've tried the form-data module in order to get the right Content-Length value using the method formData.getLengthSync()

However the problem remains the same (401 error HTTP status code response).


Solution

  • Just remove the Content-Length and Content-Type headers from your code as these headers will be set automatically by the browser.

    If you open up your network inspector, run this code snippet, and submit the form you should see that the Content-Length is set correctly:

    const foo = document.getElementById('foo')
    foo.addEventListener('submit', (e) => {
      e.preventDefault()
      const formData = new FormData(foo)
      formData.append('userId', 123)
      fetch('//example.com', {
        method: 'POST',
        body: formData
      })
    })
    <form id="foo">
      <input id="file" type="file" name="file"/><br><br>
      <button type="submit">Submit</button>
    </form>