Using Multer for File Uploads in Node.js

Uriel Rodriguez
5 min readNov 29, 2020

--

Applications become richer when they include the functionality of storing and utilizing files such as image, music, document files, and so on. And luckily Node has access to the Multer library which handles file uploads. Multer is a middleware that intercepts the POST request of the file upload to the server and works with the file in various ways before allowing the server to send out its response to the client. Some of the ways Multer works with files are by persisting the data to the file system or even validating the file, checking that certain criteria were met like file size or file type, along with many others.

To get started, we have to install the library into the project.

npm install --save multer

Next, Multer needs to be imported into your routes file. Typically in Node, each model from the database receive their own routes file, all stored within a router directory. This example will demonstrate a very popular use for file uploads: allowing users to set profile pictures.

// src/routers/user.js
// User model routes file

// User routes file configuration
const express = require('express');
const router = new express.Router();
const User = require('../models/user');
const multer = require('multer');
// Multer upload configurationconst upload = multer({
dest: 'avatars'
});

// route for uploading the profile picture
router.post('users/me/avatar', upload.single('avatar'), async (req, res) => {
const id = req.body.id;
try {
const user = await User.findById(id);
user.avatar = req.file.buffer;
await user.save();
res.send();
} catch(error) {
res.status(400).send(error);
}
}, (error, req, res, next) => {
res.status(400).send({ error: error.message });
});

There is a lot going on here so let’s break it down. The code above is written for a User model routes. The first thing to note is the imports that allow separating the User routes into its own module: “express” and “router”. Next, we import the User model from the specified relative file path. And lastly, we import the Multer library. Afterward, we configure Multer by providing an options object where we are specifying the destination or “dest” where the uploaded file will be stored, in this case, a directory called “avatars” at the root level.

The POST route for a user’s avatar consists of the basic setup, a route string, and the anonymous route handler callback, in addition to the Multer middleware, and a callback that handles errors thrown by the Multer middleware. The middleware is provided as the second argument, intercepting the POST request by executing before the route handler. The “single” method is then invoked on the “upload” object, receiving the name of the key for the key-value pair where the value is the actual file being sent via the POST request. Something to note: file uploads are handled as form-data or binary data, which means that Multer will store the file Buffer to the “avatars” directory. When trying to view the uploaded image, the file name will need to have appended the proper file type, so if the file was a jpeg, then simply append “.jpeg” to the end of the file name.

After the Multer middleware has executed and written to the file system, the route handler is executed. First, the user is located by id, passed in the POST request via the request’s body. This is ideally handled differently, especially when authentication is involved for a user trying to provide additional information for their profile. Next, a try-catch block is used which allows handling of potential errors from an asynchronous process, in this case trying to locate the user by id. If the user is found, then the uploaded file which is found on the request’s file property is stored as a buffer. The buffer data is found within the buffer property on the file property of the request. This data is then assigned to the user’s avatar property. Then the updated user is saved to the database and a default 200 response is sent. If there is an error when trying to locate the user by id, then the catch block will execute. Also, the final argument passed to the “post” method, the callback which handles errors thrown by Multer, executes in the event that the file being uploaded does not meet some criteria. After executing, the “next” argument allows the continuation of the “post” request to the route handler. In this example, there are no validations being configured for file uploads so the callback would not execute, but if it did, the error would get sent back to the client as JSON instead of Multer’s default way of registering errors via an HTML error page.

Further configurations that can be made for the Multer middleware are checking for file size and file types. So for example:

// src/routers/user.js
// User model routes file
// User routes file configurationconst express = require('express');
const router = new express.Router();
const User = require('../models/user');
const multer = require('multer');
// Multer upload configurationconst upload = multer({
limits: {
fileSize: 1000000
},
fileFilter(req, file, cb) {
if (!file.originalname.match(/\.(jpg|jpeg)$/) {
return cb(new Error('some custom error message');
}
cb(undefined, true);
}
});

In this example, we’ve only made changes to the Multer configuration. First, no longer will file uploads be stored on the file system, but instead, they will be stored on a database, which is covered in the first example. Next, a “limits” property is defined where the file’s size is specified in bytes. The file’s max size is set to 1 million bytes equating to 1 megabyte (1MB) and any file larger than that will trigger the error callback that is passed as the fourth argument to the “post” method for this route. In addition, a method available to the upload objects object is defined, the “fileFilter” which validates file types. The method receives the request, the file, which is found on the request’s file property, and a callback which either provides some result from validating the uploaded file. The “if” conditional statement accesses the property on request’s file property called “originalname” which stores the name assigned to the file, for example: “myProfilePic.jpg”. The file’s name is matched using regex which checks that the file name ends with a “.jpg” or a “.jpeg”. If there is no match, then the error callback will throw an error which will stop the POST request process from reaching the route handler callback.

So as you can see, with the Multer library, not only can you create file uploading functionality within your application but also ensure that you only work with the data that you want. Multer also provides other functionalities like storing files to the disk as well validating for other criteria. I recommend taking look at the Multer documentation for more details on that.

--

--