Axios: POST request works in Browser but not on Node

Created on 20 Jul 2017  ·  26Comments  ·  Source: axios/axios

Similar to #318 , I am unable to make a post request with axios on node. But on browser the same piece of code seems to work fine.

const fdata = new FormData();
fdata.append('user', u);
fdata.append('hostnames', n.join(' '));
const host = localStorage.getItem('host');
const port = localStorage.getItem('port');
axios({
  url: `http://${host}:${port}/hosts/remove`,
  method: 'post',
  data: fdata
}).then(response => {
  if (response.status === 200) {
    console.log(response.data);
    console.log('Removed host successfully');
  }
  return null;
}).catch(er => console.log(er));

With unirest it works:

unirest.post(`http://${host}:${port}/hosts/remove`)
.headers({ 'Content-Type': 'multipart/form-data' })
.field('user', u)
.field('hostnames', h.join(' '))
.end(response => {
  console.log(response.body);
});
  • axios version: v0.16.2
  • Environment: node v8.0.0, windows 8.1

Most helpful comment

This might be considered a duplicate of #789.

I was able to use the form-data package with Axios in nodejs. It basically provides a FormData-like interface. You have to be careful to pass the headers it generates to Axios manually, however. For example:

const axios = require('axios');
const FormData = require('form-data');

const form = new FormData();
// Second argument  can take Buffer or Stream (lazily read during the request) too.
// Third argument is filename if you want to simulate a file upload. Otherwise omit.
form.append('field', 'a,b,c', 'blah.csv');
axios.post('http://example.org/endpoint', form, {
  headers: form.getHeaders(),
}).then(result => {
  // Handle result…
  console.log(result.data);
});

All 26 comments

This might be considered a duplicate of #789.

I was able to use the form-data package with Axios in nodejs. It basically provides a FormData-like interface. You have to be careful to pass the headers it generates to Axios manually, however. For example:

const axios = require('axios');
const FormData = require('form-data');

const form = new FormData();
// Second argument  can take Buffer or Stream (lazily read during the request) too.
// Third argument is filename if you want to simulate a file upload. Otherwise omit.
form.append('field', 'a,b,c', 'blah.csv');
axios.post('http://example.org/endpoint', form, {
  headers: form.getHeaders(),
}).then(result => {
  // Handle result…
  console.log(result.data);
});

Yeah, I tried with that form-data package. Well, it's ok now, I changed the server-side API to parse JSON instead.

can someone please show what is to be done in nodejs app??

@ar412 I tried to give an example at https://github.com/mzabriskie/axios/issues/1006#issuecomment-320165427, could you clarify what you’re asking?

@binki if axios is being used to send a post request at a restapi with some data , then how to retreive that data in the rest api (which is inside an express application).

@ar412 For receiving multipart data in Express, you can use something like busboy as recommended by body-parser’s docs. Basically, for learning how to handle uploaded files in Express, that’s not related to Axios at all, so you’re better off asking elsewhere as you’ve hijacked this thread ;-). For example, see this answer on SO.

Got error message: form.getHeaders() is not a function

@binki
Hey! I have a hapi.js server in which I want to POST an image file stored in a variable by using fs.readFile(path).
I am not able to make it work sending that as FormData()

This is my code:

fs.readFile(__dirname+'/../static/lisa_server.jpg', (error, imageData) => {
          var form = new FormData()

            form.append('file', imageData,  {
            filename: 'unicycle.jpg', // ... or:
            filepath: '/../static/lisa_server.jpg',
            contentType: 'image/jpg',
            knownLength: 19806
          })

Then I send the form as the body of the request and I set options['Content-Type'] = 'multipart/form-data'

Could you help out?
Much appreciated!

@bstolarz if you pass a Buffer, which you are doing, you don’t need to pass knownLength. Also, if you read what you wrote (err, copied and pasted from form-data’s README’s examples), you should only supply one of filename or filepath—not both. The only things which it might make sense for you to set are:

  1. filename OR filepath
  2. contentType (in case the server requires a particular Content-Type value, otherwise can probably be omitted).

What I bet is happening is that the size of the image you are sending has a size that is not 19806 because that 19806 value is something you copied from an example in form-data’s docs rather than calculated for your own data. This is likely to cause form-data to throw an error itself or write invalid data in the HTTP request. Did you check if the axios.post()’s returned Promise is rejecting or if an exception is thrown? If the axios request is rejected, check if the error object has a result key and, if it does, check what the err.result.status value is. If it is a 4xx value other than 404 or 403, that probably means the server is rejecting a malformed request which could result from an incorrect known size.

@binki
Hey, thanks for your response. I corrected the things you mentioned, so now my code is

fs.readFile(__dirname+'/../static/lisa_server.jpg', (error, imageData) => {
          var form = new FormData()

          form.append('file', imageData, {
               filepath: __dirname+'/../static/lisa_server.jpg',
               contentType: 'image/jpg'

          })

But the server throws 411 "Length is required" (I also tried fs.createReadStream which appears to be fully supported by FormData, but I get the same length error).

@binki
I managed to set the header content-length in the lowest layer and I don't get that error anymore.
Now that I don't get a 411 from the server, I got back to the error I once had 400- "No Files Provided".

This is what the request looks like

Starting Request

{ adapter: [Function: httpAdapter],
  transformRequest: { '0': [Function: transformRequest] },
  transformResponse: { '0': [Function: transformResponse] },
  timeout: 5000,
  xsrfCookieName: 'XSRF-TOKEN',
  xsrfHeaderName: 'X-XSRF-TOKEN',
  maxContentLength: -1,
  validateStatus: [Function: validateStatus],
  headers:
   { common: { Accept: 'application/json, text/plain, */*' },
     delete: {},
     get: {},
     head: {},
     post: { 'Content-Type': 'application/json' },
     put: { 'Content-Type': 'application/json' },
     patch: { 'Content-Type': 'application/json' },
     'User-Agent': 'trojan server 1.0',
     'X-Origin-Panamera': 'Staging',
     'Content-Length': 25247 },
  baseURL: 'https://letgoar-a.akamaihd.net/api/v1',
  method: 'post',
  url: 'https://<baseDomain>/api/v1/images',
  data:
   FormData {
     _overheadLength: 218,
     _valueLength: 25247,
     _valuesToMeasure: [],
     writable: false,
     readable: true,
     dataSize: 0,
     maxDataSize: 2097152,
     pauseStreams: true,
     _released: false,
     _streams:
      [ '----------------------------677738213014296377492349\r\nContent-Disposition: form-data; name="file"; filename="/Users/brenda/repos/qreator2/qreator/trojan-server/src/static/lisa_server.jpg"\r\nContent-Type: image/jpg\r\n\r\n',
        <Buffer ff d8 ff e0 00 10 4a 46 49 46 00 01 01 01 00 48 00 48 00 00 ff e2 11 2c 49 43 43 5f 50 52 4f 46 49 4c 45 00 01 01 00 00 11 1c 61 70 70 6c 02 00 00 00 ... >,
        [Function: bound ] ],
     _currentStream: null,
     _boundary: '--------------------------677738213014296377492349' },
  'Content-Type': 'multipart/form-data' }

Hope you check this out. To you, does this look like a reasonable request?

Thanks in advance

@bstolarz I have done my best to reproduce your issue. However, I can’t. I wrote this code to try to imitate what you’re doing. However, I purposefully omitted manually setting Content-Length. I am convinced that the fact that you are setting Content-Length might be related to your issue. Please try to let the form-data take care of calculating that for you—that is why you must call form.getHeaders().

See this paste: https://gist.github.com/binki/10ac3e91851b524546f8279733cdadad . Perhaps you can modify the way you call axios.post() or axios() to match my example and it will work for you?

If you have issues still, please try moving your code to its own script and reproducing it there. It may help you to solve the issue. If you are still stuck, please post full code, including the call to axios() or axios.post() as a gist and link it here and, if I can, I will look into it.

It still doesn't work at all...

@rodrigogs If you want help, you’ll need to be more detailed ;-).

I followed this helpful research on Axios and the final solution works for me (link)

Here is the copy / paste of it:

import fs from 'fs';
import FormData from 'form-data';
import axios from 'axios';

let data = fs.createReadStream(__dirname + '/test.jpg');
let form = new FormData();

form.append('type','image');
form.append('media',data,'test.jpg');

function getHeaders(form) {
    return Promise((resolve, reject) => {
        form.getLength((err, length) => {
            if(err) { reject(err); }
            let headers = Object.assign({'Content-Length': length}, form.getHeaders());
            resolve(headers);
         });
    });
}

getHeaders(form)
.then((headers) => {
    return axios.post(url, form, {headers:headers})
})
.then((response)=>{
    console.log(response.data)
})
.catch(e=>{console.log(e)})

@westofpluto, i get the same error too. @binki, any thoughts?
The error
form.getHeaders is not a function

@smplyjr Can you give more context and let me know how you get form? Without code, we can’t tell what you’re doing or help you.

For nodejs users solve by using the querystring lib, as follows:

const querystring = require('querystring')
axios
  .post(URL, querystring.stringify(data))
  .then((response) => ...)
  .catch((error) => ...)

As @heldrida mentioned, use querystring. That is the way axios recommends doing it as well here: https://www.npmjs.com/package/axios#nodejs. The form-data package has all sorts of issues here and you'll just end up pulling your hair out trying to get it to work.

@heldrida @ashok-sc How do I need to use querystring or qs to upload a file with axios? I'm using axios to upload files from an AWS Lambda and I obviously don't have access to File object from there

@bstolarz I have done my best to reproduce your issue. However, I can’t. I wrote this code to try to imitate what you’re doing. However, I purposefully omitted manually setting Content-Length. I am convinced that the fact that you are setting Content-Length might be related to your issue. Please try to let the form-data take care of calculating that for you—that is why you must call form.getHeaders().

See this paste: https://gist.github.com/binki/10ac3e91851b524546f8279733cdadad . Perhaps you can modify the way you call axios.post() or axios() to match my example and it will work for you?

If you have issues still, please try moving your code to its own script and reproducing it there. It may help you to solve the issue. If you are still stuck, please post full code, including the call to axios() or axios.post() as a gist and link it here and, if I can, I will look into it.

Thanks man. Addiing Content-Length solved my problem now I can use axios backend-2-backend calls with FormData:
const options = { method: 'POST', url: myUrl, data: justJsonBody, transformRequest: [function (data, headers) { const formData = convertToFormData(data); // returrns ForrmData from form-data headers['Content-Type'] = formData.getHeaders()['content-type']; headers['Content-Length'] = formData._overheadLength; return formData; }] };

This might be considered a duplicate of #789.

I was able to use the form-data package with Axios in nodejs. It basically provides a FormData-like interface. You have to be careful to pass the headers it generates to Axios manually, however. For example:

const axios = require('axios');
const FormData = require('form-data');

const form = new FormData();
// Second argument  can take Buffer or Stream (lazily read during the request) too.
// Third argument is filename if you want to simulate a file upload. Otherwise omit.
form.append('field', 'a,b,c', 'blah.csv');
axios.post('http://example.org/endpoint', form, {
  headers: form.getHeaders(),
}).then(result => {
  // Handle result…
  console.log(result.data);
});

Another way here. In this way, you can add proxy agent to and other configs:

const axios = require('axios');
const FormData = require('form-data');
const ProxyAgent = require('proxy-agent');

const form = new FormData();
// Second argument  can take Buffer or Stream (lazily read during the request) too.
// Third argument is filename if you want to simulate a file upload. Otherwise omit.
form.append('field', 'a,b,c', 'blah.csv');
axios({
  method: 'POST',
  url: 'http://example.org/endpoint',
  data: form,
  agent: new ProxyAgent("https://username:[email protected]:8080"),
  headers: bodyFormData.getHeaders()
}).then(result => {
  // Handle result…
  console.log(result.data);
});

This works for me.

// ES6
import axios from 'axios';
import FormData from 'form-data';
import fs from 'fs';

FormData.prototype.getHeadersWithContentLength = function getHeadersWithContentLength() {
  return new Promise((resolve, reject) => {
    this.getLength((err, length) =>
      err ? reject(err) : resolve({ ...this.getHeaders(), 'Content-Length': length })
    )
  })
}

const payload = new FormData();
const form = new formidable.IncomingForm();

form.parse(req, (err, fields, { file }) => {
  if (err) return;

  payload.append("file", fs.createReadStream(file.path), {
    filename: file.name,
    contentType: file.type
  });

  payload.getHeadersWithContentLength().then(headers => {
    api
      .post(endpoint, payload, { headers })
      .then(({ data }) => data)
      .then(data => res.json({ data }));
  });
});

Thanks to all the contributors of this post. After spending hours of having issues POSTing my form-data form using Axios, to an backend that posts it to an Amazon bucket, the solution turned out to manually set content-length....

For anyone else having issues my code ended up like, maybe it can help the next in line having issues getting this to work :)

const axios = require('axios');
const FormData = require('form-data');

// Where buffer is a file
formData.append('file', buffer);

// Added a promise version like seen in earlier comments to get this
const contentLength = await formData.getLength();

await axios(`<ENDPOINT>`, {
    method: 'POST',
    baseURL: <BASE_URL>,
    params: {
        fileName: '<FILE_NAME>.png'
    },
    headers: {
        authorization: `Bearer <TOKEN>`,
        ...formData.getHeaders(),
        'content-length': contentLength
    },
    data: formData
});

After spending hours of having issues POSTing my form-data form using Axios, to an backend that posts it to an Amazon bucket, the solution turned out to manually set content-length....

Yes. Me and a colleague of mine also spent several hours when trying to POST files to a backend that would reply that no data was being submitted, while it obviously was, since we could trace the request and see the content. The problem was the missing content-length header.

As a note, if you're adding a buffer to the FormData, it's fine if you call formData.getLengthSync() but if you're dealing with a stream, you have to first stat the file with fs.statSync(filePath).size or get the full size another way (like from a content-length header from an upstream), for example if the stream comes from a request or socket or whatever. If your stream comes from the disk instead, fs.statSync(filePath).size will give the size in bytes, so you can add it in knownLength when appending to FormData:

formData.append("file", fs.createReadStream(filePath), { filename: 'whatever.pdf', knownLength: fs.statSync(filePath).size });

Of course all Sync methods could be switched for async ones and the await keyword.

// Added a promise version like seen in earlier comments to get this
const contentLength = formData.getLength();

I guess you forgot an await before formData indeed...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

shaosh picture shaosh  ·  3Comments

Baoyx007 picture Baoyx007  ·  3Comments

samayo picture samayo  ·  3Comments

helmus picture helmus  ·  3Comments

achingbrain picture achingbrain  ·  3Comments