Upload File with NestJS

A tutorial on how to upload files to a local folder and online storage

Ryan Adhitama Putra
Bits and Pieces

--

Uploading is one of the basic skills of backend developers. In this tutorial, I wanna explain uploading files into local folders and online storage such as Cloudinary.

Cloudinary is a SaaS technology company headquartered in Santa Clara, California, with offices in Israel, England, Poland, and Singapore. The company provides cloud-based image and video management services. It enables users to upload, store, manage, manipulate, and deliver images and videos for websites and apps.

Requirement

Before we go so far, let’s prepare some software and account we need.

Software

Account

Setup Project

Install NestJS CLI globally:

$ npm install -g @nestjs/cli

Create a new project and test it:

$ nest new nest-upload
$ cd nest-upload
$ npm run start:dev

The output will be like this:

Route: (/local) (POST & Form Data)

  • First of all, we need to install the main package for upload named multer
$ npm i @types/multer
  • Create folder public/img at the root folder
folder public/img
  • Add a function local to handle uploaded files saved in public/img folder
import {
Controller,
Post,
UploadedFile,
UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';

@Controller()
export class AppController {

@Post('local')
@UseInterceptors(
FileInterceptor('file', {
storage: diskStorage({
destination: 'public/img',
filename: (req, file, cb) => {
cb(null, file.originalname);
},
}),
}),
)
async local(@UploadedFile() file: Express.Multer.File) {
return {
statusCode: 200,
data: file.path,
};
}

}
  • Then, test the endpoint via Postman
success request

Let’s access the file http://localhost:3000/public/img/download.png

The server detects the file as a route, so we need to install the package to serve a static file.

$ npm i @nestjs/serve-static

Import ServeStaticModule at AppModule (src/app.module.ts)

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';

@Module({
imports: [
ServeStaticModule.forRoot({
rootPath: join(__dirname, '..'),
})
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

Let’s access the file again, and the server will return a static file.

serving static file

Route: (/online) (POST & Form Data)

$ npm i cloudinary buffer-to-stream
  • Create Cloudinary module and service
$ nest g mo cloudinary
$ nest g s cloudinary
  • Add uploadImage function at CloudinaryService (src/cloudinary/cloudinary.service.ts). Update aaa, bbb, and ccc with your Cloudinary's credential
import { Injectable } from '@nestjs/common';
import { UploadApiErrorResponse, UploadApiResponse, v2 } from 'cloudinary';
import toStream = require('buffer-to-stream');

@Injectable()
export class CloudinaryService {
async uploadImage(
fileName: Express.Multer.File,
): Promise<UploadApiResponse | UploadApiErrorResponse> {
return new Promise((resolve, reject) => {
v2.config({
cloud_name: 'aaa',
api_key: 'bbb',
api_secret: 'ccc',
});
const upload = v2.uploader.upload_stream((error, result) => {
if (error) return reject(error);
resolve(result);
});
toStream(fileName.buffer).pipe(upload);
});
}
}
  • Export CloudinaryService at CloudinaryModule (src/cloudinary/cloudinary.module.ts)
import { Module } from '@nestjs/common';
import { CloudinaryService } from './cloudinary.service';

@Module({
providers: [CloudinaryService],
exports: [CloudinaryService],
})
export class CloudinaryModule {}
  • Import CloudinaryModule at AppModule (src/app.module.ts)
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CloudinaryModule } from './cloudinary/cloudinary.module';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';

@Module({
imports: [
ServeStaticModule.forRoot({
rootPath: join(__dirname, '..'),
}),
CloudinaryModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
  • Add a function online to handle uploading files into Cloudinary
import {
Controller,
Post,
UploadedFile,
UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { CloudinaryService } from './cloudinary/cloudinary.service';
@Controller()
export class AppController {
constructor(private cloudinary: CloudinaryService) {}

@Post('local')
@UseInterceptors(
FileInterceptor('file', {
storage: diskStorage({
destination: 'public/img',
filename: (req, file, cb) => {
cb(null, file.originalname);
},
}),
}),
)
async local(@UploadedFile() file: Express.Multer.File) {
return {
statusCode: 200,
data: file.path,
};
}

@Post('online')
@UseInterceptors(FileInterceptor('file'))
async online(@UploadedFile() file: Express.Multer.File) {
return await this.cloudinary
.uploadImage(file)
.then((data) => {
return {
statusCode: 200,
data: data.secure_url,
};
})
.catch((err) => {
return {
statusCode: 400,
message: err.message,
};
});
}
}

💡 Pro Tip: You can now encapsulate the file upload logic into a package and push it to Bit as an independently tested and versioned component. This would let you reuse this component across multiple projects.

Learn more here:

  • Test via Postman
success request

Conclusion

Today we have learned how to create Upload File with NestJS. Stay on top of several other news, let’s connect via LinkedIn!

https://www.linkedin.com/in/ryanadhitama/

Build Apps with reusable components, just like Lego

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo

--

--