How to upload images and videos to Cloudinary using Node.js

by Marow Macaulay

.

Updated Mon Apr 24 2023

How to upload images and videos to Cloudinary using Node.js

How to upload images and videos to Cloudinary using Node.js from our frontend can be a challenging task for beginners.

In this article, we all are going to learn one of the most fundamental and crucial features in any web/mobile application.

We learn how to upload images and videos to Cloudinary using node.js and also save it in our local server using best practice and industry standards.

We will discuss useful packages that make our development process easier and faster, and we are also going to learn how to connect our server to the Cloudinary CDN for storing images and videos sent from our server.

PREREQUISITE

  • Any IDE of your choice.
  • Fundamental knowledge of JavaScript and it’s runtime environment Node.js.
  • A Cloudinary account.
  • POSTMAN for testing our API.
  • MongoDB for storing the data of our files.
  • And of course a basic knowledge of software development.

So let us quickly get started.

Setting up a new project with expressjs.


Firstly, you will need to create a folder named fileUpload in any desired directory of your choice.

Well, I’m using my desktop directory for this process.

[backenders@kali Desktop]$ mkdir fileUpload

Then you change the directory to the newly created folder directory and create your entry file.

[backenders@kali fileUpload]$  touch app.js

Now that your entry file has been created, we are going to start writing the necessary code to handle the file upload process to the Cloudinary server.

But before that, you will need to install some vital packages that will help to make the development faster. These packages include:

  • Express.js
  • Cloudinary
  • Multer
  • Mongoose
  • Path

We can install all these by writing the following code in the terminal of our project directory.

npm install express cloudinary multer mongoose path dotenv --save

After a successful installation, we can now go ahead and start writing the code for the server setup.

In the app.js file, you can write the below codes to set up the server.


const express = require('express'),
      app = express(),
      PORT = 3000;
app.get('/server',(req,res)=>{
  res.send('Server seems to be working')
}).listen(PORT, ()=>{
  console.log(`Server is running on ${PORT}`);
})

In the project directory, we can start the server by running node app in the terminal.

We are good to go if we see on our terminal:

Server is running on 3000

To verify we can easily go to our browser and check, by going to http://localhost:3000/server, if it matches what we have below or whatever message you displayed then it is working fine.

Alright, we can go ahead and complete the code to implement our first feature.

Creating your routes

In the app.js file, we are going to create an endpoint that will be used to post the images that are going to be sent to the cloudinary server.


const express = require('express'),
      app = express(),
      PORT = 3000;
app.get('/server',(req,res)=>{
    res.send('Server seems to be working') 
});
app.post('/postImage',(req,res)=>{
  res.send('Images posted successfully')
}).listen(PORT, ()=>{console.log(`Server is running on ${PORT}`)})

Now that a post route has been created, we are going to implement the logic for handling that route.

Upload Image to Cloudinary using node.js

Create your Image models

We are going to create 4 folders called controllers, routes, config, models.

In your project directory:

[backenders@kali fileUpload]$  mkdir routes controllers config models

Change directory to the model’s folder and create a new file called imageModel.js.

This file is going to contain the logic of how each image file data is going to be stored on our database.

Inside the file you can write these codes:


const mongoose = require('mongoose'),
      {Schema} = mongoose,
      imageSchema = new Schema({
        imageName: {
          type: String,
          required: true
          },
        imageId: {
          type: String,
          },
        imageUrl: {
          type: String,
        }
     });

module.exports = mongoose.model("Image", imageSchema)

Now that the imageSchema has been set up, we can now go and set the configuration file for cloudinary.

Configure Cloudinary

Before we do that, we need to login to our Cloudinary account at https://cloudinary.com/users/login and grab our Cloud name, API Key, and API secret.

Those credentials are needed in order for us to have access to our Cloudinary resources such as the images and videos stored in our account.

Now that you have those details, go to the config folder and create a file called cloudinaryConfig.js.


require("dotenv").config();
const cloudinary = require("cloudinary");
cloudinary.config({
  cloud_name: process.env.CLOUD_NAME || YOUR_CLOUD_NAME,
  api_key: process.env.API_KEY || YOUR_API_KEY,
  api_secret: process.env.API_SECRET || YOUR_API_SECRET,
});
exports.uploads = (file) => {
  return new Promise((resolve) => {
    cloudinary.uploader.upload(
      file,
      (result) => {
        resolve({ url: result.url, id: result.public_id });
      },
      { resource_type: "auto" }
    );
  });
};

N/B: Replace YOUR_CLOUD_NAME with the actual cloud name from Cloudinary and API_KEY with your actual API key from Cloudinary the same with API_SECRET.

To explain these few lines of code, we made a Cloudinary config file that contained our credentials in order to post images and videos to the Cloudinary server and we also exported a function called uploads.

This function takes in a parameter called file (your image/video file) and uploads it with the uploader.upload() method from the Cloudinary package.

We made the function return a new Promise object that would contain the resolved object which is our uploaded file url and the id of the file.

Building the upload logic

Now let us go to our controllers and fix things up over there.

In our controllers, we are going to create an Error handler firstly.

Then go ahead to wrap our imageController.

In the controllers, folder create a file called errorHandler.js, inside that file, you are going to create a middleware that handles error in our code.


module.exports = {
  err404: (req,res,next) => {
      const error = new Error('Not Found');
      error.status = 404
      next(error)
    },
  err500 : (error,req,res) => {
    res.status(error.status || 500).send({
      error: {
      status: error.status || 500,
      message: error.message || "Internal Server Error"
      }
    })
  }
}

Now that our error handler is set up already, we can go ahead to create the imageController.js file.

In the file, you can write:


const imageModel = require("../models/imageModel");
//IMPORT CLOUDINARY CONFIG
const cloud = require("../config/cloudinaryConfig");

module.exports = {
  createImage: (req, res) => {
    let imageDetails = {
      imageName: req.files[0].originalname,
    };
    //USING MONGODB QUERY METHOD TO FIND IF IMAGE-NAME EXIST IN THE DB
    imageModel.find({ imageName: imageDetails.imageName }, (err, callback) => {
      //CHECKING IF ERROR OCCURRED.
      if (err) {
        res.json({
          err: err,
          message: `There was a problem creating the image because: ${err.message}`,
        });
      } else {
        let attempt = {
          imageName: req.files[0].originalname,
          imageUrl: req.files[0].path,
          imageId: "",
        };
        cloud.uploads(attempt.imageUrl).then((result) => {
          let imageDetails = {
            imageName: req.files[0].originalname,
            imageUrl: result.url,
            imageId: result.id,
            clientId: req.body.clientId,
            clientUsername: req.body.clientUsername,
          };
          // Create image in the database
          imageModel
            .create(imageDetails)
            .then((image) => {
              res.json({
                success: true,
                data: image,
              });
            })
            .catch((error) => {
              res.json({
                success: false,
                message: `Error creating image in the database: ${error.message}`,
              });
            });
        });
      }
    });
  },
}

So a method has been created for posting an image to the cloudinary server.

Before uploading the image, it checks the local database if the file already exists by using the MongoDB query find, and if the file doesn’t we upload the file using cloudinary.uploads from the function, we created earlier and post it to the Cloudinary.

If the file was uploaded successfully, then we create the data on our local database to store the details of the file.

Setting up Multer

We can now go back to the config file and create a configuration for multer which we would use to create disk storage where the files would be kept on the server.

Go to the config folder and create a multerConfig.js file.


const multer = require("multer"),
  path = require("path");
//multer.diskStorage() creates a storage space for storing files.
const imageStorage = multer.diskStorage({
  destination: (req, file, cb) => {
    if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
      cb(null, path.join(__dirname, "../files"));
    } else {
      cb({ message: "This file is not an image file" }, false);
    }
  },
  filename: function (req, file, cb) {
    cb(null, file.originalname);
  },
});
module.exports = {
  imageUpload: multer({ storage: imageStorage })
};

What multer does is just for us to have a store where we can store the files on the server.

It makes use of the directory specified to store the files and in this case, it was specified to store the images in a folder called files.

So we can quickly create an empty folder called files in your project directory.

Now that we have that already, we are going to do a final touch by creating an index route so as to keep our application organized and structured.

Change your directory to the routes folder and create 3 files called errorRoutes.js, imageRoutes.js and index.js.

We are going to start writing codes for each file.

For the first file errorRoutes.js:


const router = require("express").Router(),
  errorHandler = require("../controllers/errorHandler");

router.use(errorHandler.err404, errorHandler.err500);

module.exports = router;

This file makes use of the errorHandler file create in the controllers folder and that was why it was required at the top of our file.

We also need to use Express Router in order to handle the routing for this handler and specifically told express to use this controller as a middleware, that was why you saw router.use(...)

We will go ahead to the next file imageRoutes.js:


const upload = require("../config/multerConfig"),
  imageController = require("../controllers/imageController"),
  router = require("express").Router();

router
  .post("/postImages", upload.imageUpload.any(), imageController.createImage)

module.exports = router;

In this file, we made use of the multerConfig file to the first check if the file is of the right format and also store it locally before uploading it to the Cloudinary server.


Finally, we go to our index.js file, this file organizes our routes file and it is used as our entry point in our main app.js file:



const router = require("express").Router(),
  errorRoutes = require("./errorRoutes"),
  imageRoutes = require("./imageRoutes");

router
  .get("/server", (req,res)=>{
  res.send('Server seems to be working')
 })
  .use("/images", imageRoutes)
  .use(errorRoutes);

module.exports = router;

So we are definitely good to go 🙂

All we need to do now is refactor our app.js file to use the index route file.

In our app.js file: 


const express = require("express"),
  app = express(),
  mongoose = require("mongoose"),
  router = require("./routes/index");

require("dotenv").config();
MONGO_URI = process.env.MONGO_URI || "mongodb://localhost:27017/image_files";
mongoose.Promise = global.Promise;
mongoose.connect(MONGO_URI, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});
// Assign Mongoose connection to the variable db
let db = mongoose.connection;
db.on("open", () => {
  console.log("Connected to MongoDB using Mongoose");
});
app
  .use(express.urlencoded({ extended: false }))
  .use(express.json())
  //Index route
  .use("/", router)
  .set("port", process.env.PORT || 3000)
  .listen(app.get("port"), () => {
    console.log(`Server running at http://localhost:${app.get("port")}`);
  });

Testing our Image uploading service

Now you can go ahead to start the server using node app, after a successful message on the terminal.

You can open Postman to test the endpoint.

In your postman, you are going to create a POST request to http://localhost:3000/images/postImages

In this POST request, you are uploading an image file with the key imageName and the image contents.

If the image uploaded successfully, you can check the response from the postman.

So there you have it, a successful response detailing the imageUrl, imageName and the imageId.

All these details are stored in MongoDB, you can verify by checking it in MongoDB-Compass(GUI to check your MongoDb instance and collections).

You can also check your cloudinary account to verify if the image was uploaded successfully.

Upload Videos to Cloudinary using Node.js


Now that the image upload controller has been set up successfully, we can now move on to the video upload.

Create A Video Controller

Create a new file in the Controllers folder called videoController.js, In the file:


const Video = require("../models/videoModel"),
  cloud = require("../config/cloudConfig");
module.exports = {
  // Create action for a new video
  create: (req, res, next) => {
    // First check if the file exists in the Database
    let test = {
      name: req.files[0].originalname,
      url: req.files[0].path,
      id: "",
    };
    console.log(req.files[0].originalname);
    Video.find({ name: test.name }, (err, cb) => {
      if (err) {
        res.json({
          error: true,
          message: `There was a problem uploading the video because: ${err.message}`,
        });
      } else {
        let file = {
          name: req.files[0].originalname,
          url: req.files[0].path,
          id: "",
        };
        cloud
          .uploads(file.url)
          .then((result) => {
            Video.create({
              name: req.files[0].originalname,
              url: result.url,
              id: result.id,
            });
          })
          .then((result) => {
            res.json({
              success: true,
              data: result,
            });
          })
          .catch((err) => {
            res.json({
              error: true,
              message: err.message,
            });
          });
      }
    });
  },
}

This is similar to the imageController.js file we created earlier.

Create A Video Model

We are going to create a model to synchronise our controller with, let’s get to it.

In the model’s folder, create a new file called videoModel.js, inside that file write the following code:


const mongoose = require("mongoose"),
  { Schema } = mongoose,
  videoSchema = new Schema(
    {
      name: {
        type: String,
        required: true,
      },
      url: {
        type: String,
      },
      id: {
        type: String,
        unique: true,
      },
    },
    {
      timestamps: true,
    }
  );
module.exports = mongoose.model("Video", videoSchema);

Configure Multer for Video

Now we are all done with the video model, we just need to configure multer again but this time to accept only videos.

In the config folder, go to multerConfig.js file, inside that file we are going to add the video configuration file:


const videoStorage = multer.diskStorage({
  destination: (req, file, cb) => {
    if (file.mimetype === "video/mp4") {
      cb(null, path.join(__dirname, "../files"));
    } else {
      cb({ message: "This file is not in video format." }, false);
    }
  },
  filename: (req, file, cb) => {
    cb(null, file.originalname);
  },
});

Our multerConfig.js file should look exactly as what we have below.


const multer = require("multer"),
  path = require("path");
//multer.diskStorage() creates a storage space for storing files.
const imageStorage = multer.diskStorage({
  destination: (req, file, cb) => {
    if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
      cb(null, path.join(__dirname, "../files"));
    } else {
      cb({ message: "This file is not an image file" }, false);
    }
  },
  filename: function (req, file, cb) {
    cb(null, file.originalname);
  },
});
const videoStorage = multer.diskStorage({
  destination: (req, file, cb) => {
    if (file.mimetype === "video/mp4") {
      cb(null, path.join(__dirname, "../files"));
    } else {
      cb({ message: "This file is not in video format." }, false);
    }
  },
  filename: (req, file, cb) => {
    cb(null, file.originalname);
  },
});
module.exports = {
  imageUpload: multer({ storage: imageStorage }),
  videoUpload: multer({ storage: videoStorage }),
};

Create A Video Route

Finally, we are going to create routes to handle the video upload to Cloudinary, in your routes folder, create a new file called videoRoutes.js and input the following code:


const videoController = require("../controllers/videoController"),
  upload = require("../config/multerConfig"),
  router = require("express").Router();

router
  .post("/postVideo", upload.videoUpload.any(), videoController.create)

module.exports = router;

Now that we have this ready, we can link it up to routes/index.js, so the complete index.js the file should look like:


const router = require("express").Router(),
  errorRoutes = require("./errorRoutes"),
  videoRoutes = require("./videoRoutes"),
  imageRoutes = require("./imageRoutes");
router
  .get("/",(req,res)=>{
  res.send("Server seems to be working")
})
  .use("/images", imageRoutes)
  .use("/videos", videoRoutes)
  .use(errorRoutes);

module.exports = router;

Testing our Video Upload Service.

You can test this on the postman, with the following URL http://localhost:3000/videos/postVideo


You can verify it on MongoDb and Cloudinary.

Conclusion

So far, we have been able to create an application that uploads images and videos using Node.js.

Do you have a way you achieve this task in your day to day activities, let’s hear it in the comment sections below.

Thanks!!!

Whenever you're ready

There are 4 ways we can help you become a great backend engineer:

The MB Platform

Join 1000+ backend engineers learning backend engineering. Build real-world backend projects, learn from expert-vetted courses and roadmaps, track your learnings and set schedules, and solve backend engineering tasks, exercises, and challenges.

The MB Academy

The “MB Academy” is a 6-month intensive Advanced Backend Engineering BootCamp to produce great backend engineers.

Join Backend Weekly

If you like post like this, you will absolutely enjoy our exclusive weekly newsletter, Sharing exclusive backend engineering resources to help you become a great Backend Engineer.

Get Backend Jobs

Find over 2,000+ Tailored International Remote Backend Jobs or Reach 50,000+ backend engineers on the #1 Backend Engineering Job Board

Backend Tips, Every week

Backend Tips, Every week