How To Upload An Image To A Node Express API

In this tutorial we will explore using a package called multer, which provides a node express middleware, to upload images to our ExpressJS API.

To upload the images, I will be using sample code from a component written in VueJS, but asides from syntax, the process is identical in vanilla JavaScript and any other front-end framework. I use VueJS for this demo because the syntax is easier to understand in snippets, and Vue offers additional features out of the box which would otherwise require jQuery or a similar library. Familiarity with VueJS is not required to follow along. The focus for this tutorial is on Node Express.

How to upload an image to a Node Express API using Multer

Step 1: Start with a blank NodeJS project with express already installed and set up. I use nodemon during development for a better experience - and you should consider it too! If you're unfamiliar with getting started with Node Express, you can check out my Super Simple NodeJS repo on GitHub. Clone it locally and it will have everything in place to get started.

The first step is to install the required packages with the following command:

npm install multer --save
# for this tutorial I used multer v1.4.1

Step 2: The next step is to configure the multer middleware. In NodeJS projects you will typically see this done this at the top of the file that defines the API routes, however, as demonstrated in my Super Simple NodeJS repo I think it is better to configure such middleware in it's own configuration file. For developers new to Node, like myself, it makes your code a lot more modular and easier to debug, re-use and understand.

So, create a new file called config/multer.js and add the following code:

const multer  = require('multer');

const diskStorageToUploads = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, 'uploads')
    },
    filename: (req, file, cb) => {
        cb(null, file.originalname)
    }
});

const saveToUploads = multer({storage: diskStorageToUploads});

module.exports = {
    saveToUploads: saveToUploads.single('file')
}

On the first line, the code imports the multer package using the require statement, and assigns it to a local variable called multer.

Since the variable is not going to change during runtime, it's wise to define it as a const instead of let or var. This will require you to be running Node v8 or higher.

Next, using the diskStorage() method in the multer library, a destination for the file upload is defined as well as a file name. For this example, the file is saved to an uploads directory in the root of the NodeJS project; and the filename is saved as the original filename provided by the user when uploading.

Finally, a multer instance with the above storage configuration is created within another local variable and then exported from the multer.js config file as part of an ES6 module export.

This configuration can now be imported in to any other file within our project using the require statement, as seen later in this guide.

Though some may advocate that this isn't the best way of configuring multer, from my experience, it offers a good balance of modular code and an easier approach for less experienced developers.

Step 3: As an example, the test route will be defined within the project's server.js file. But in larger projects this should be abstracted to separate API/router files.

Within the server.js file add the following code:

const express = require('express');
const app = express();
const router = express.Router();
const multerConfig = require("./config/multer");

router.post('/test', multerConfig.saveToUploads, (req, res) => {
    return res.json("file uploaded successfully");
});

app.use(router);

app.listen(port, () => {
  console.log('We are live on ' + port);
});

Your prior configuration in the server.js file may be slightly different - and this is fine. The key here is to import the multer config file created earlier in this guide, and to include the saveToUploads() method as middleware to a route. You can see in the above code snippet that a route /test is defined and the middleware is added using multerConfig.saveToUploads.

At this point you can save your changes and run your project. It should fire up without any errors, and using Postman you should be able to send a POST request to /test a receive back a success message.

Step 4: The multer middleware works by handling only those requests which are multipart (multipart/form-data). This means that any type of POST request with no multipart/form-data will pass right through the middleware and the function bound to the route will handle the request.

The easiest platform to upload files is through a webform. The following code uses a VueJS component to build a very simple webform with an input field and a button. As mentioned at the top of this post the same approach - but with different syntax - will work for Angular, React, Vanilla JavaScript and any other front-end frameworks.

Below is my VueJS component for uploading a file with the HTML elements and JavaScript logic defined:

<template>
    <input type="file" name="file" ref="myFileRef">
    <button type="button" v-on:click="uploadFile()">Upload!</button>
</template>

<script>
export default {
    name: 'FileUpload',
    methods: {
        uploadFile: function () {
            let file = this.$refs.myFileRef.files[0];

            if (!file) {
                console.error("no file selected");
                return;
            }

            let formData = new FormData();

            formData.append('file', file);

            return this.$http.post(
                `http://localhost:8000/test`,
                formData,
                {
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    }
                }
            )
            .then(response => {
                console.info("file uploaded successfully");
            })
            .catch(error => {
                console.error("file upload failed", error);
            });
        }
    }
}
</script>

In this component, when the user selects a file using the file-input and clicks the Upload button the uploadFile() function is executed. This function retrieves the page's global $refs object and then locates the property myFileRef. This property name must match with with attribute given to the <input> element (ref="myFileRef"). It then locates the first file in this object and assigns it to the variable file.

Next, a FormData object is created and the reference to the file is appended. This FormData object is then used as the payload in a POST request to the API, with the headers of the request set to multipart/form-data (otherwise the multer middleware will not pick it up).

The .then() and .catch() are used on the Promise to provide some visual feedback in the console as to whether the HTTP call succeeded.

Step 5: Back on the server side, the uploaded file should now be available in the uploads directory as defined in config/multer.js. If the request failed server-side with a weird error message, there is a high likelihood that the uploads folder does not exist and for this example you can create it manually.