Image Upload to Cloudinary Using NodeJS & TypeScript.
Tags: webdev, programming, tutorial, node
Published: 2024-01-30 18:32:24

## 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](https://cloudinary.com/documentation/upload_images) 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: 
 
```javascript
npm i multer sharp cloudinary
```
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:

```javascript
npm i @types/multer
```

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:

```javascript
    app.use(express.json());
    app.use(express.urlencoded({extended:true}));
```
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:

```javascript
npm i dotenv 
```

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


```javascript
 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);
  }
};
```
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:

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

```
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:

```javascript
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});
    }
});
```

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.
```javascript
["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]
```

## 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](https://cloudinary.com/documentation/upload_images)

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](https://linkedin.com/in/emmanuel-samson-eneche)