Photo by Andrew Neel on Unsplash
How To Add Image Upload Functionality To A Node JS API Using Multer and Mongo DB
Table of contents
- Prerequisites
- 1. Create A Mongoose Schema Or Update Your Mongoose Schema To Support Files
- 2. Create The Multer Storage Function And File Size Converter Function
- 3. Use The Exported blogImages Function
- 4. Work On The Files Array
- 5. Send The File Data To The Database
- 6. Make Request Using Postman or Any API Client
As part of an exam I wrote a few months back, I had to write an API using Node JS and Express. I decided to improve the API by adding image upload functionality using Multer. Multer is a Node JS middleware for handling multipart/form-data which is primarily used for uploading files. I'll explain how to add that functionality to a Node JS API below.
Prerequisites
Before starting this tutorial:
Ensure you've set up a Node JS Server
Ensure a Mongo DB database is connected to your API
Ensure you've created a route using the post method
Install Multer
Have Knowledge of Mongoose Schema
Have Knowledge of JavaScript
After the prerequisites are set, follow these steps
1. Create A Mongoose Schema Or Update Your Mongoose Schema To Support Files
The first thing I did was update my Mongoose schema to support files. This can simply be done by setting an array in the mongoose schema to be an object.
files: [Object]
After updating my Mongoose schema with the code above, my Mongoose schema became
const mongoose = require("mongoose");
const BlogSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
unique: true,
},
description: {
type: String,
required: true,
},
tags: {
type: Array,
required: true,
},
author: {
type: String,
required: true,
},
state: {
type: String,
required: true,
enum: ['draft', 'published'],
default: "draft"
},
read_count: {
type: Number,
default: 0,
},
reading_time: {
type: Number
},
body: {
type: String,
required: true,
unique: true
},
files: [Object]
},
{ timestamps: true }
);
module.exports = mongoose.model("Blog", BlogSchema);
2. Create The Multer Storage Function And File Size Converter Function
I wrote both the Multer Storage Function and the File size converter function in the same file(uploadFunction.js) before exporting them. You can write the functions in separate files if you wish. Both functions can be written by following these steps:
1. Import Multer
const multer = require("multer");
Set storage destination and file name
const multer = require("multer"); const Storage = multer.diskStorage({ destination: "blogImages", filename: (req, file, cb) => { cb(null, new Date().toISOString().replace(/:/g, "-") + "-" + file.originalname); } })
Multer comes with a diskStorage function that sets the destination (folder) where files are stored and sets the name which the files are stored with.
From the code above, we set the destination of the uploaded files to be a new folder called "blogImages". Multer will create this folder and send all uploaded images there. The filename method sets the name a file is stored with when uploaded using Multer. Multer gives us access to a file object and we use the file's original name from the file object together with the date to create a new unique name for each file.
Set File Type
const multer = require("multer"); const Storage = multer.diskStorage({ destination: "blogImages", filename: (req, file, cb) => { cb(null, new Date().toISOString().replace(/:/g, "-") + "-" + file.originalname); } }) const fileFilter = (req, file, cb) => { if (file.mimetype === "image/png" || file.mimetype === "image/jpg"|| file.mimetype === "image/jpeg") { cb(null, true); } else { cb(null, false); } }
From the code above, we set the file type using the fileFilter function. We used it to define the type of images our API should accept.
Pass both the storage and fileFilter functions to Multer
const multer = require("multer"); const Storage = multer.diskStorage({ destination: "blogImages", filename: (req, file, cb) => { cb(null, new Date().toISOString().replace(/:/g, "-") + "-" + file.originalname); } }) const fileFilter = (req, file, cb) => { if (file.mimetype === "image/png" || file.mimetype === "image/jpg"|| file.mimetype === "image/jpeg") { cb(null, true); } else { cb(null, false); } } const blogImages = multer({ storage: Storage, fileFilter: fileFilter });
Both the Storage and fileFilter functions are passed into Multer and set to a constant which will be exported later
Write a function to convert the file size
const fileSizeConverter = (bytes, decimalPlaces) => { if (bytes === 0) { return "0 Bytes"; } const dP = decimalPlaces || 2; const storageSizes = ["Bytes", "KB", "MB", "GB", "TB"]; const index = Math.floor(Math.log(bytes) / Math.log(1024)); const calculatedSize = parseFloat((bytes / Math.pow(1024, index)).toFixed(dP) + " " + storageSizes[index]); const sizeAndMemory = calculatedSize + " " + storageSizes[index] return sizeAndMemory; }
Multer by default saves files in Bytes. Using the function above, we can convert that to the appropriate file size.
Export the fileSizeConverter function and the blogImages constant
module.exports = {blogImages, fileSizeConverter}
The code in our file should look like this
const multer = require("multer");
const Storage = multer.diskStorage({
destination: "blogImages",
filename: (req, file, cb) => {
cb(null, new Date().toISOString().replace(/:/g, "-") + "-" + file.originalname);
}
})
const fileFilter = (req, file, cb) => {
if (file.mimetype === "image/png" || file.mimetype === "image/jpg"|| file.mimetype === "image/jpeg") {
cb(null, true);
} else {
cb(null, false);
}
}
const blogImages = multer({
storage: Storage, fileFilter: fileFilter
});
const fileSizeConverter = (bytes, decimalPlaces) => {
if (bytes === 0) {
return "0 Bytes";
}
const dP = decimalPlaces || 2;
const storageSizes = ["Bytes", "KB", "MB", "GB", "TB"];
const index = Math.floor(Math.log(bytes) / Math.log(1024));
const calculatedSize = parseFloat((bytes / Math.pow(1024, index)).toFixed(dP) + " " + storageSizes[index]);
const sizeAndMemory = calculatedSize + " " + storageSizes[index]
return sizeAndMemory;
}
module.exports = {blogImages, fileSizeConverter}
3. Use The Exported blogImages Function
Import both functions where you created a route using the post method as specified earlier in the tutorial. Pass the blogImages function into the route as shown below
authorBlogRoute.post('/', blogImages.array("files", 10), async (req, res) =>
The array method for blogImages is used to add multiple files to the API at the same time. The "files" argument is the key with which the files will be uploaded while the "10" argument refers to the maximum number of files that can be uploaded at once.
4. Work On The Files Array
When uploading multiple files, Multer creates an array and stores the files in that array. In your route created earlier, write the following function
let filesArray = [];
req.files.forEach( element => {
const file = {
fileName: element.originalname,
fileType: element.mimetype,
fileSize: fileSizeConverter(element.size, 2)
}
filesArray.push(file);
})
In the function above, we first create an empty array. We then use the forEach method on the files array created by Multer to set the fileName, fileType and fileSize. We use the imported fileSizeConverter function to convert the file size. We then push our updated array to the empty array created earlier.
5. Send The File Data To The Database
first import the Mongoose schema you created earlier.
const blogModel = require("../models/blog.model");
You set the files method created in the Mongoose schema earlier to the filesArray just created before sending it to the database. Mine for my blog API is shown below
const blogDetails = {
title: body.title,
description: body.description,
tags: body.tags,
author: blogAuthor,
reading_time,
body: blogArticle,
files: filesArray
}
await blogModel.create(blogDetails)
.then((blog) => {
return res.json({ status: true, blog }).status(201)
}).catch((err) => {
return res.json({ status: false, message: err }).status(403)
})
Note that the code above is written in the route with the post method.
6. Make Request Using Postman or Any API Client
Ensure the body of the request is passed in as form data.
Remember to pass in "files" as the key value for the files to be uploaded.
Check your Mongo DB database to confirm file information
The 3 images were successfully uploaded to the blogImages folder as specified earlier.
To see the source code you can check my git hub repo here
Happy coding ✌...