Cloudinary is a powerful serverless cloud-based storage infrastructure that allows easy file uploading for NestJS projects. In this article, we will explore the various methods of file uploading to Cloudinary and how to set it up in your NestJS project. Whether you are uploading file buffers, file URLs, or Base64 encoded strings, the Cloudinary NodeJS SDK and the steps outlined in this article make it easy to incorporate advanced file uploading into your NestJS project.
If you don't know how to handle file upload in NestJs, this article explains how to efficiently incorporate file upload and validation into your NestJS project.
Creating a Cloudinary Module
One way to easily set it up in your NestJs projects is by creating a module for it. This allows you to use Dependency Injection design to use it in other providers or controllers.
Installation
Install Cloudinary NodeJs SDK
npm install cloudinary
Create a new module, cloudinary
.
Create file constants.ts
.
export const CLOUDINARY = 'Cloudinary';
Add file cloudinary.provider.ts
, paste the code below into the file.
import { ConfigService } from '@nestjs/config';
import { v2 } from 'cloudinary';
import { CLOUDINARY } from './constants';
export const CloudinaryProvider = {
provide: CLOUDINARY,
useFactory: (config: ConfigService) => {
return v2.config({
cloud_name: config.get('CLOUDINARY_CLOUD_NAME'),
api_key: config.get('CLOUDINARY_API_KEY'),
api_secret: config.get('CLOUDINARY_API_SECRET'),
});
},
inject: [ConfigService],
};
ConfigService is used to assess keys in your secret file. For example, a .env
file.
Don't know how to set up env configurations in NestJs? Read more.
Don't know how to create factory providers in NestJs? Read more.
The CloudinaryProvider
provider is a Factory Provider. The useFactory
allows you to create providers dynamically. You can also inject other module-imported services into the useFactory
function.
Create the Cloudinary Service Injectable
Add a new file, cloudinary.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class CloudinaryService {
}
This service will contain methods for uploading files to Cloudinary.
Create the module file
Add a new file, cloudinary.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { CloudinaryProvider } from './cloudinary.provider';
import { CloudinaryService } from './cloudinary.service';
@Module({
imports: [ConfigModule],
providers: [CloudinaryProvider, CloudinaryService],
exports: [CloudinaryProvider, CloudinaryService],
})
export class CloudinaryModule {}
The ConfigModule
is added to the module imports because ConfigService
in the CloudinaryProvider
.
Setup .env file
Create a .env
in your root folder. Depending on how you structure your project, a general root folder could be the folder containing your package.json
file or where you run the command to start your server.
CLOUDINARY_CLOUD_NAME=test
CLOUDINARY_API_KEY=test
CLOUDINARY_API_SECRET=test
Now you need to create a free Cloudinary account. After creating your account, you can follow this guide to get your API key, secret and cloud name.
Setup ConfigModule
In your app.module.ts
you will need to configure the ConfigModule
. This makes sure it finds the .env
file and populate itself with the keys and values, thereby making you access them with ConfigService
.
import {
Module,
NestModule,
} from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot(),
],
controllers: [AppController],
providers: [
AppService,
],
})
export class AppModule implements NestModule {
}
Using Form data - File Buffers
Single File Upload
Now that you have your CloudinaryService
. When you create a route that accepts file buffers using Multer. You can upload this file using the service. You just have to integrate the upload method from the Cloudinary package.
Add the method below to your service.
async uploadFile(
file: Express.Multer.File,
): Promise<UploadApiResponse | UploadApiErrorResponse> {
return new Promise((resolve, reject) => {
v2.uploader.upload_stream(
{
resource_type: 'auto',
},
(error, result) => {
if (error) return reject(error);
resolve(result);
}
).end(file.buffer)
})
}
This simple method uses v2.uploader.upload_stream
to upload your file to Cloudinary in chunks, which is good for uploading large files in chunks instead of one long request.
An UploadApiResponse
or UploadApiErrorResponse
is returned by the function. You can use this to know if the file was successfully uploaded or not.
To access your uploaded file URL, use the example of a route for single file upload below to understand the process.
import { UploadedFile, UseInterceptors } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
@Post('upload/single')
@UseInterceptors(FileInterceptor('file'))
@ApiOkResponse({ description: 'Upload image', type: String })
public async uploadSingleImage(@UploadedFile() file: Express.Multer.File) {
const result = await this.cloudinaryService.uploadFile(file);
return result.secure_url;
}
The secure_url
property is the URL for the file uploaded.
Uploading multiple file buffers
Loop through all the files and upload each one after the other. The response is the list of secure URLs.
async uploadFiles(
files: Express.Multer.File[],
) {
const urls = await Promise.all(files.map(async (file): Promise<string> => {
const { secure_url } = await this.uploadFile(file);
return secure_url;
}));
return urls
}
The files were mapped over and uploaded, and their URLs were returned by the map callback function.
Uploading File with URLs
What if you have a file URL and not a file buffer? It's simpler to upload that using Cloudinary.
async uploadFileFromUrl(
url: string,
): Promise<UploadApiResponse | UploadApiErrorResponse> {
return v2.uploader.upload(url)
}
The URL passed to this would be uploaded to Cloudinary, which will then return a response containing the secure_url
if successful.
Uploading multiple file urls
By following the same idea from uploading multiple file buffers. You can map through the urls, upload each and return the secure_url
for each. Then the function will return a list of URLs.
async uploadFilesFromUrl(
urls: string[],
) {
return Promise.all(urls.map(async (url: string): Promise<string> => {
const { secure_url } = await this.uploadFileFromUrl(url);
return secure_url;
}));
}
Uploading Base64 encoded strings
Base64 is a binary-to-text encoding scheme that represents binary data in an American Standard Code for Information Interchange (ASCII) string format. An example of binary data is an image. Base64 can be uploaded too as a file, it's a string and not a buffer, which makes it easier to parse and upload to Cloudinary than file buffers.
By utilizing the upload
function from the Cloudinary package, a base64 string can be uploaded to Cloudinary and get a URL for the uploaded file.
async uploadFileFromBase64(
data: string,
): Promise<UploadApiResponse | UploadApiErrorResponse> {
return v2.uploader.upload(data)
}
async uploadManyBase64(
files: string[],
) {
const urls = await Promise.all(files.map(async (file): Promise<string> => {
const { secure_url } = await this.uploadFileFromBase64(file);
return secure_url;
}));
return urls
}
Conclusion
In conclusion, Cloudinary is a powerful tool for file uploading and storage in NestJS projects. With the help of the Cloudinary NodeJS SDK and the steps outlined in this article, developers can easily set up a module for Cloudinary and upload files using various available methods including file buffers, file URLs, and Base64 encoded strings. While Cloudinary is free to set up initially, developers may need to pay for used services once they start using it past some limit.
I'd love to connect with you on Twitter | LinkedIn | GitHub | Portfolio
See you in my next blog article. Take care!!!
Top comments (0)