DEV Community

Cover image for Image Upload to Cloudinary Using NodeJS & TypeScript.
Emmanuel Eneche
Emmanuel Eneche

Posted on • Updated on

Image Upload to Cloudinary Using NodeJS & TypeScript.

Introduction

When building applications that requires a lot of asset management, particularly images, you are then saddled with the responsiblity of ensuring that these assets (images) can be served to your users at a lightning-fast experience with improved performance, we then consider a popular tool known as Cloudinary

What Then is Cloudinary?

Cloudinary is a cloud-based image and video management service that provides a comprehensive solution for managing, optimizing, and delivering media files on the web. It offers a variety of features for developers and businesses to handle their images and videos efficiently, including storage, optimization, transformation, and delivery.

Problem Statement

The traditional way of managing images/assets in complex applications, like an E-commerce store where vendors are often required to upload multiple image files for a single product directly to the server, many times leads to storage and scalability drawbacks, with its attendant impact on performance. This article will be providing a solution to this challenge using Cloudinary in a Node & TypeScript project.

This article assumes you already have your Node and TypeScript project all setup and so the rest sections will guide you on better ways to organize your code to clear the task.

Solution

To begin, in your existing Node and TypeScript project, head over to the terminal and install the following packages using the below command:

npm i multer sharp cloudinary
Enter fullscreen mode Exit fullscreen mode

The above command will install multer sharp & cloudinary to your project. We need to also install multer types to provide information to multer library using:

npm i @types/multer
Enter fullscreen mode Exit fullscreen mode

Next, in your app.ts or server.ts or index.ts, whatever the name of the entry point to your application is, please ensure you have the following block added:

    app.use(express.json());
    app.use(express.urlencoded({extended:true}));
Enter fullscreen mode Exit fullscreen mode

Now we have the application ready to accept incoming requests with file attachment plus we now have easy integration with cloudinary. Also, we will install dotenv package to enable us access cloudinary config in our .env file , hence we run the command:

npm i dotenv 
Enter fullscreen mode Exit fullscreen mode

Next, we create a simple middleware to handle one or more image upload request on our server like this;

 import dotenv from "dotenv"
 import multer, { Multer } from 'multer';
 import { v2 as cloudinary, UploadApiResponse, 
 UploadApiErrorResponse } from 'cloudinary';
 import sharp from 'sharp';


 dotenv.config();

cloudinary.config({
  cloud_name: process.env.CLOUDINARY_NAME, 
  api_key: process.env.CLOUDINARY_API_KEY, 
  api_secret: process.env.CLOUDINARY_SECRET,
});

interface CloudinaryFile extends Express.Multer.File {
  buffer: Buffer;
}

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

export const uploadToCloudinary = async (req: Request, res: Response, next: NextFunction) => {
  try {
    const files: CloudinaryFile[] = req.files as CloudinaryFile[];
    if (!files || files.length === 0) {
      return next(new Error('No files provided'));
    }
    const cloudinaryUrls: string[] = [];
    for (const file of files) {
      const resizedBuffer: Buffer = await sharp(file.buffer)
        .resize({ width: 800, height: 600 })
        .toBuffer();

      const uploadStream = cloudinary.uploader.upload_stream(
        {
          resource_type: 'auto',
          folder: 'your-cloudinary-folder-name',
        } as any,
        (err: UploadApiErrorResponse | undefined, result: UploadApiResponse | undefined) => {
          if (err) {
            console.error('Cloudinary upload error:', err);
            return next(err);
          }
          if (!result) {
            console.error('Cloudinary upload error: Result is undefined');
            return next(new Error('Cloudinary upload result is undefined'));
          }
          cloudinaryUrls.push(result.secure_url);

          if (cloudinaryUrls.length === files.length) {
            //All files processed now get your images here
            req.body.cloudinaryUrls = cloudinaryUrls;
            next();
          }
        }
      );
      uploadStream.end(resizedBuffer);
    }
  } catch (error) {
    console.error('Error in uploadToCloudinary middleware:', error);
    next(error);
  }
};
Enter fullscreen mode Exit fullscreen mode

From the above function, we have defined and exported two key functions which will be needed in our image/item upload router. There are: upload & uploadToCloudinary functions. Also, you must have noticed that we used sharp to resize the original dimension of the image which you can change to your own preference at any time here:

  const resizedBuffer: Buffer = await sharp(file.buffer)
        .resize({ width: your-width, height: your-height })
        .toBuffer();

Enter fullscreen mode Exit fullscreen mode

Ok, Our image upload middleware is all set, next we head over to our router and call on the uploadToCloudinary & upload functions previously exported like:

import {upload, uploadToCloudinary } from '../../libraries/cloudinary'; //the file path where you had written this functions in earlier

imageUploadRouter.post("/upload", upload.array('images', 5),  uploadToCloudinary, async (req: Request, res: Response) => {
    try {
        const cloudinaryUrls = req.body.cloudinaryUrls;
        if (cloudinaryUrls.length === 0) {
            console.error('No Cloudinary URLs found.');
            return res.status(500).send('Internal Server Error');
        }
       const images = cloudinaryUrls;
       return res.send(images)

    } catch (error) {
        return res.status(500).json({ error});
    }
});
Enter fullscreen mode Exit fullscreen mode

Finally, we now have our image files uploaded to cloudinary and the variable images in the function above, is an array that contains the URLs of all images previously uploaded to cloudinary.

["https://res.cloudinary.com/dyppbuosu/image/upload/v1706286166/Your-folder-name/yn80wlwv7sfaoj0bqrla.png1", https://res.cloudinary.com/dyppbuosu/image/upload/v1706286166/Your-folder-name/yn80wlwv7sfaoj0bqrla.png1]
Enter fullscreen mode Exit fullscreen mode

Summarizing

You have used Typescript and Node to handle file upload to cloudinary. Hence, managing asset files especially images with cloudinary can help your project with improved performance and increased page load speed, scalable and secure cloud storage, eliminating the need for on-premises storage solutions, developer-Friendly APIs and SDKs amongst many other benefits. To get deeper insights on Cloudinary, you can learn more here:
Cloudinary guide

Did you encounter any challenge so far? don't sweat it, let me know in the comment section. You can also reach out to me on LinkedIn

Top comments (0)