In this article, we will go through the steps involved in uploading images to the cloud using Node.js, and we will be using Cloudinary as our cloud storage.
This article will go through the steps involved in uploading images to cloud storage with Node.js, and the cloud storage service we will be using is Cloudinary. When it comes to creating and delivering your digital assets with the least amount of effort, Cloudinary has you covered with an end-to-end management solution.
This post assumes that you are familiar with the basics of JavaScript and Node.js.
To follow along, I recommend you have the following:
Let’s set up our development environment. Open your terminal and run the following command.
mkdir proEnv
cd proEnv
yarn init
You’ll be asked a few questions, and your answers will be used to generate your package.json file.
Next, we need to install Express—a framework for Node.js.
yarn add express
Let’s create a file named app.js
at the root of our project that will serve as an entry point. Navigate to your parent folder and run the command below.
touch app.js
Now, add the following code inside the app.js
file:
const express = require("express")
const app = express()
const port = 9000
app.use(express.json())
app.listen(port, () => {
console.log(`Server is running on port:${port}`)
})
Run the following command in your terminal to start your server:
node app.js
You should see a message in your terminal saying, “Server is running on port 9000.” To automate our server modifications, track changes and restart the server whenever a change occurs, we need to install nodemon as a dev dependency.
yarn add -D nodemon
For the package to work as expected, let’s add a script to our package.json
file.
"scripts": {
"dev": "nodemon app.js",
"start": "node app.js"
}
Your package.json
file should look like this:
{
"name": "image-upload-api",
"version": "1.0.0",
"description": "Uploading Images to Cloudinary",
"main": "app.js",
"author": "Sproff",
"license": "MIT",
"scripts": {
"dev": "nodemon app.js",
"start": "node app.js"
},
"dependencies": {
"express": "^4.17.1"
}
}
Create a folder at the root of your project called utils
. Inside the folder, create a file named errorHandler.js
and add the following to it:
class ErrorHandler extends Error {
constructor(statusCode, message) {
super();
this.statusCode = statusCode;
this.message = message;
}
}
module.exports = {
ErrorHandler,
};
We created a global error handler to avoid duplicating or rewriting functions that’ll handle errors that may arise while uploading an image or failing to follow a required step. It assigns a status Code
and a message
depending
on the request.
Require dotenv
in your app.js
file by adding the following:
require('dotenv').config()
Create a file at the root of your project named .env
. Go to your Cloudinary dashboard, and copy and paste your Cloud name
, API Key
and API Secret
.
CLOUDINARY_CLOUD_NAME=xxx
CLOUDINARY_API_KEY=xxx
CLOUDINARY_API_SECRET=xxx
At the root of your project, create a folder called controller
. Inside the folder, create a file named upload.controller.js
and add the following code to it:
const { ErrorHandler } = require('../utils/errorHandler')
const uploadImage = async (req, res, next) => {
try {
res.json({
status: 'success',
message: 'Upload successful',
})
} catch (error) {
next(new ErrorHandler(error.statusCode || 500, error.message))
}
}
module.exports = {
uploadImage,
}
We passed three parameters to the uploadImage
function. It returns the result of a successful image upload and handles errors with the error handler we created earlier.
Next, we need to install the following packages. Open your terminal and run the following command:
yarn add multer cloudinary dotenv
The command above will install Multer, which is middleware used to handle requests with multipart/form-data
header and Cloudinary. Both packages will help us upload our image effectively. We also installed dotenv
,
which is an npm package we’ll use to keep our configurations private.
At the root of your project, create a folder named service
. Inside the folder, create a file named upload.service.js
and add the following to it:
const multer = require("multer");
const cloudinary = require("cloudinary");
const { ErrorHandler } = require("../utils/errorHandler");
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
const memoryStorage = multer.memoryStorage();
const upload = multer({
storage: memoryStorage,
});
const uploadToCloudinary = async (fileString, format) => {
try {
const { uploader } = cloudinary;
const res = await uploader.upload(
`data:image/${format};base64,${fileString}`
);
return res;
} catch (error) {
throw new ErrorHandler(500, error);
}
};
module.exports = {
upload,
uploadToCloudinary,
};
Multer allows us to effortlessly transfer files from a browser to the server we desire. Without Multer, we won’t be able to access any data because the request body will be empty.
In the code above, we configure Cloudinary with our credentials stored in the .env
file. The process.env
property will have access to the keys defined there. We create a function called uploadToCloudinary
that accepts
the buffer that needs to be processed before uploading the image to Cloudinary. The upload
function serves as middleware that will be called whenever you want to run Multer, and it returns basic information such as the type of storage.
In our case, we’re using MemoryStorage
.
Multer allows us to store files dynamically in two ways—DiskStorage
and MemoryStorage
. We’re using MemoryStorage
because it is the most common kind of storage, and it allows us to communicate with our
server while keeping the buffer in memory for future use.
Next, we need to install a package that will allow us to convert between formats—in our case, buffer to data URI. Open your terminal and run the following command:
yarn add datauri
Inside your utils
folder, create a file named file.js
and add the following to it:
const DatauriParser = require('datauri/parser')
const parser = new DatauriParser()
const bufferToDataURI = (fileFormat, buffer) =>
parser.format(fileFormat, buffer)
module.exports = {
bufferToDataURI,
}
The DatauriParser
function here acts as a file format conversion passage. We have a file called buffer
that needs to be converted to data URI before it can be uploaded to Cloudinary because Cloudinary doesn’t know what
buffer
is. The parser will look for the file format .png
or .jpg
, and convert the buffer to a string.
Now replace the code you have in your upload.controller.js
file with this updated one:
const { uploadToCloudinary } = require("../service/upload.service");
const { ErrorHandler } = require('../utils/errorHandler')
const { bufferToDataURI } = require('../utils/file')
const uploadImage = async (req, res, next) => {
try {
const { file } = req
if (!file) throw new ErrorHandler(400, 'Image is required')
const fileFormat = file.mimetype.split('/')[1]
const { base64 } = bufferToDataURI(fileFormat, file.buffer)
const imageDetails = await uploadToCloudinary(base64, fileFormat)
res.json({
status: 'success',
message: 'Upload successful',
data: imageDetails,
})
} catch (error) {
next(new ErrorHandler(error.statusCode || 500, error.message))
}
}
module.exports = {
uploadImage,
}
We’re importing the bufferToDataURI
function in the code above and selecting the file format we want to accept. We destructured the file name from the request body and passed it through a simple check to avoid errors. We get the fileFormat
from the file object, split it to select a specific type, and pass the buffer to the bufferToDataURI
function, which returns a base64
string.
uploadToCloudinary
function here accepts the fileFormat
and the base64
string. After a successful upload, res
provides a response with all of the details of the image.
Let’s create our route. At the root of your project, create a folder named routes
, and inside it, create a file named upload.routes.js
. Add the following to the file:
const { Router } = require('express')
const { uploadImage } = require('../controller/upload.controller')
const { upload } = require('../service/upload.service')
const router = Router()
router.post('/', upload.single('image'), uploadImage)
module.exports = router
Here we destructured the Express router and assigned it to a variable to generate a post request. We’re using the Multer middleware, and upload.single
simply uploads a single image. There are other methods for uploading images, such
as upload.array
, which can upload multiple images.
Now, let’s import our newly created route into our app.js
file.
const express = require("express");
require("dotenv").config();
const app = express();
const uploadRouter = require('./routes/upload.routes')
const port = process.env.PORT || 9000;
app.use(express.json());
app.use('/upload', uploadRouter)
app.listen(port, () => {
console.log(`Server running on port: ${port}`);
});
To view the image you just uploaded, log in to your Cloudinary account and check your media library.
This post covered the steps involved in uploading images directly to Cloudinary using Express.js and Multer. We saw how easy it was to integrate Cloudinary with these third-party libraries.