Understanding the File Upload Middleware with Cloudinary in Node.js
Tags: multer, express, node, backenddevelopment
Published: 2024-02-22 04:53:22

In this article, we will explore a middleware for file uploads in a Node.js application, which leverages Cloudinary for image storage. The middleware is written in TypeScript and uses the `multer` library for handling `multipart/form-data`, which is primarily used for uploading files.

## Setting Up Cloudinary

Cloudinary is a cloud-based service that provides an end-to-end solution for image and video management, including uploads, storage, manipulations, optimizations, and delivery.  

The Cloudinary configuration is set up at the beginning of the code:

```
cloudinary.config({
  cloud_name: config.CLOUD_NAME,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET,
});
```

The `cloud_name` is your unique Cloudinary account identifier. The `api_key` and `api_secret` are your Cloudinary account credentials, which should be kept secure and not exposed in your code.

## The Upload Function

The `uploadToCloudinary` function is responsible for uploading a file to Cloudinary:

```
const uploadToCloudinary = (path: string, imageName: string) => {
  // ...
};
```

This function takes two parameters: the local path of the image to be uploaded (`path`) and the desired public ID for the image on Cloudinary (`imageName`). If the `path` is not provided, an error is thrown.

The function returns a Promise that resolves with the result of the upload or rejects with an error. After the upload is complete, the local file is deleted using the `fs.unlink` function.

## Multer Middleware

Multer is a middleware for handling `multipart/form-data`, which is primarily used for uploading files. It is used to process the incoming request and store the uploaded files in the local filesystem.

The `storage` object defines where to store the uploaded files and how to name them:

```
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, process.cwd() + "/uploads/");
  },
  filename: function (req, file, cb) {
    const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() *  1e9);
    cb(null, file.fieldname + "-" + uniqueSuffix);
  },
});
```

The `destination` function determines the folder where the uploaded files will be stored. The `filename` function generates a unique name for each uploaded file.

Finally, the `upload` middleware is created using the defined `storage`:

```
export const upload = multer({ storage: storage });
```

This middleware can now be used in an Express route to handle file uploads. When a file is uploaded, it will be stored in the specified directory and then uploaded to Cloudinary.


## Code at a glance 
```
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-var-requires */
import { v2 as cloudinary } from "cloudinary";
import multer from "multer";
import config from "../config";
import AppError from "../errors/customError";
const fs = require("fs");

cloudinary.config({
  cloud_name: config.CLOUD_NAME,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET,
});

const uploadToCloudinary = (path: string, imageName: string) => {
  if (!path) {
    throw new AppError("File path does not found!", 400);
  }

  return new Promise((resolve, reject) => {
    try {
      cloudinary.uploader.upload(
        path, //local image path
        { public_id: imageName },
        function (error, result) {
          if (error) {
            reject(error);
          }
          resolve(result);

          // removing image file from 'path' of image
          fs.unlink(path, (err: any) => {
            if (err) {
              throw new AppError("Error in deleting file after uploads", 400);
            }
          });
        },
      );
    } catch (error: any) {
      throw new AppError(error.message, 400);
    }
  });
};

export default uploadToCloudinary;

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, process.cwd() + "/uploads/");
  },
  filename: function (req, file, cb) {
    const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
    cb(null, file.fieldname + "-" + uniqueSuffix);
  },
});

export const upload = multer({ storage: storage });

```
## Conclusion

This middleware provides a robust solution for handling file uploads in a Node.js application. By integrating Cloudinary, it offloads the responsibility of storing and serving images, allowing the application to focus on other tasks. The use of Promises and async/await makes the code easier to read and understand, and the use of `multer` simplifies the handling of file uploads.