Upload File with NestJS
A tutorial on how to upload files to a local folder and online storage
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
- NodeJS (https://nodejs.org/en)
- Visual Studio Code (https://code.visualstudio.com/download)
- Postman (https://www.postman.com)
Account
- Cloudinary (https://cloudinary.com)
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
- 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
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.
Route: (/online) (POST & Form Data)
- Install Cloudinary and buffer-to-stream package
$ 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
Conclusion
Today we have learned how to create Upload File with NestJS. Stay on top of several other news, let’s connect via LinkedIn!
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.
Split apps into components to make app development easier, and enjoy the best experience for the workflows you want: