Send a File With Axios in Node.js
Tags: node, webdev, codenewbie, programming
Published: 2021-05-31 16:46:27

*This article was originally published a day earlier at https://maximorlov.com/send-a-file-with-axios-in-nodejs/*

Programatically sending requests in Node.js can be a frustrating experience. First, you have to choose one out of many request libraries in the ecosystem. Second, they each have a slightly different API which is confusing when you're switching.

You also have to make sure the request is formatted in a specific way for the 3rd party on the receiving end to accept it.

Just as you're starting to get the hand of axios, you soon find out there are (subtle) usage differences depending on whether you're in the browser or Node.js.

What a pain.

Only if sending files with axios in Node.js would be as easy as taking a walk in the park.

Well, it can be.

In this article, you'll learn how to send files and associated data by constructing a form. We'll cover the two file types — Buffers and Streams, and how to work with them.

## Construct a form with form-data library
Before sending a file with axios, you first need to create a form and append the file to it. Axios can be used both in the frontend as backend and the library doesn't differentiate between the two. Therefore, sending a file with axios in Node.js is similar to sending a file with axios in the browser.

Because we don't have access to the `FormData` interface in Node.js as we do in the browser, we use the [form-data library](https://github.com/form-data/form-data) to construct a form. This is similar to a `<form>` element with encoding type set to `"multipart/form-data"` in the browser.

To construct a form, create a new instance and use the `append(name, value)` method to add a file and additional fields.

```js
// `form-data` library gives us a similar API in Node.js to the `FormData` interface in the browser
const FormData = require('form-data');

// Create a new form instance
const form = new FormData();

// Append text fields to the form
form.append('productName', 'Node.js Stickers');
form.append('productDescription', 'Cool collection of Node.js stickers for your laptop.');

// `file` can either be a Buffer or a Stream
// ⚠️ don't forget the 3rd argument, the file name, when appending a file
form.append('productImage', file, 'stickers.jpg');
```

Notice that when adding a file to the form, the append function takes three arguments instead of two. The third argument is the file name and if it's missing, the file won't send properly so make sure to pass it along.

The second argument is the file itself, which can either be a Buffer or a Stream. Let's look at a few real examples of how and why you would use either two.

### File as a Buffer
A file buffer (or blob) is what you'll encounter most often when dealing with files. It's essentially the entire file stored in binary format in the application's memory.

If you're familiar with [multer](https://github.com/expressjs/multer), it uses `MemoryStorage` by default which is essentially storing the files in memory as a Buffer. Reading a file from disk with `fs.readFile()` also gives you the file stored as a Buffer.

In both cases, you can append the file buffer to the form:

```js
const form = new FormData();

// File parsed by multer from incoming request
const file = req.file;
form.append('file', file.buffer, file.originalname);

// or read from disk
const file = await fs.readFile('./my-image.jpg');
form.append('file', file, 'my-image.jpg');
```

### File as a Stream
You can also append a file as a stream to the form. This is useful when, for example, the file is fetched from an external resource. You can then proxy the file directly to another API without storing it locally.

```js
// Fetch external image as a stream
const response = await axios.get('https://i.imgur.com/8uJcFxW.jpg', { responseType: 'stream' });

const form = new FormData();
// Pass image stream from response directly to form
form.append('image', response.data, 'kitten.jpg');
```

Another good example is when you're dealing with large files. Using streams instead of buffer could prevent your application from consuming too much memory and eventually crashing.

```js
// Open file as a readable stream
const fileStream = fs.createReadStream('./large-file.zip');

const form = new FormData();
// Pass file stream directly to form
form.append('largeFile', fileStream, 'large-file.zip');
```

To send multiple files, you simply `append` them one by one to the form.

## Send the form with axios
Now let's send the form with axios. The axios API for sending a POST request is:

`axios.post(url[, data[, config]])`, where:

- `url` - server URL that will be used for the request
- `data` (optional) - the data to be sent as the request body
- `config` (optional) - configuration object where you can set the request headers, amongst others

> While the second and third arguments are optional, their order *is* important. Axios will always assume the second argument is the `data` you want to send with the request. It's a common mistake to pass the `config` object as the second argument instead of the *third* argument.

**To send a form with axios in Node.js, you have to grab the form boundary and add it to the request.**

The `getHeaders()` method on the form returns an object with `Content-Type` header set to `multipart/form-data` plus a [unique boundary](https://stackoverflow.com/a/20321259/4763270):

```js
// form.getHeaders() gives you the Content-Type header with a unique boundary
console.log(form.getHeaders());
// {
//   'content-type': 'multipart/form-data; boundary=--------------------------339261229255605205279691'
// }
```

> When you create a form instance, internally it generates and uses a unique boundary. If you set the Content-Type header with a random boundary you created, it won't match the form's boundary and the request won't parse correctly. Therefore, make sure to always use the form's boundary.

Use the destructuring assignment to set the Content-Type header in the `config` parameter (3rd argument). This allows you to add additional headers if you wish to do so.

Here's how to send a form with axios:

```js
// When using axios in Node.js, you need to set the Content-Type header with the form boundary
// by using the form's `getHeaders()` method
const response = await axios.post(url, form, {
  headers: {
    ...form.getHeaders(),
    Authorization: 'Bearer ...', // optional
  },
});
```

> Manually setting the Content-Type header is *only* needed in the backend. In the frontend, the browser does this automatically for you so you shouldn't set Content-Type yourself otherwise the file won't send properly.

## Everything put together
To summarise, sending a file with axios in Node.js requires you to do two important things:

1. Create a form with the form-data library
2. Grab the Content-Type header with the form's boundary with `form.getHeaders()` and assign it to the axios request

We've looked at different ways to `append` a file to a form, either as a Buffer or a Stream. Below is a complete example that reads a file from disk into a Buffer and sends it to an external API with axios.

```js
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs/promises');

// Read image from disk as a Buffer
const image = await fs.readFile('./stickers.jpg');

// Create a form and append image with additional fields
const form = new FormData();
form.append('productName', 'Node.js Stickers');
form.append('productDescription', 'Cool collection of Node.js stickers for your laptop.');
form.append('productImage', image, 'stickers.jpg');

// Send form data with axios
const response = await axios.post('https://example.com', form, {
  headers: {
    ...form.getHeaders(),
    Authentication: 'Bearer ...',
  },
});
```

## How do you upload a file in Node.js?

Use the FREE request parsing guide and implement seamless working Node.js APIs that follow the latest best practices.

👉🏼 [Grab your copy of the FREE Request Parsing in Node.js Guide](https://maximorlov.com/request-parsing-nodejs-guide/)