# [LEARNING] Nest.js ## Nest ``` nest new [Project Name] ``` ## TypeORM ``` yarn add @nestjs/typeorm typeorm yarn add pg ``` #### init config ``` typeorm init --database postgres ``` ``` { "type": "postgres", "host": "localhost", "port": 5432, "username": "postgres", //輸入預設的root帳號 "password": "xxx", // 密碼 "database": "users", // 指定資料庫名稱 "synchronize": true, "logging": false, "entities": [ // mapping class的放的位置,指定放在shared下 "src/shared/entity/**/*.ts" ], "migrations": [ // 存放資料庫版本管控(migration)的檔案,指定放在shared下 "src/shared/migration/**/*.ts" ], "subscribers": [ "src/subscriber/**/*.ts" ], "cli": { //預設使用CLI產生檔案的目錄,指定放在shared資料夾下 "entitiesDir": "src/shared/entity", "migrationsDir": "src/shared/migration", "subscribersDir": "src/shared/subscriber" } } ``` #### entity ``` typrorm entity:create -n User ``` ## Postgres ``` docker run --rm --name mypostgres -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 -d postgres https://myapollo.com.tw/2018/07/10/docker-postgres/ psql -U postgres docker exec -it 059 psql -U postgres ``` ## Pipe Validation Pipe ``` yarn add class-validation class-transformer ``` ```typescript= import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common'; import { validate } from 'class-validator'; import { plainToClass } from 'class-transformer'; @Injectable() export class ValidationPipe implements PipeTransform<any> { async transform(value: any, { metatype }: ArgumentMetadata) { if (!metatype || !this.toValidate(metatype)) { return value; } const object = plainToClass(metatype, value); const errors = await validate(object); if (errors.length > 0) { throw new BadRequestException('Validation failed'); } return value; } private toValidate(metatype: Function): boolean { const types: Function[] = [String, Boolean, Number, Array, Object]; return !types.includes(metatype); } } ``` In DTO ```typescript= import { IsEmail, IsString, Length } from 'class-validator'; export class UserDTO { @IsString() @Length(0, 10, { message: 'length less than 10', }) username: string; @IsEmail() email: string; } ``` In Controller: ```typescript= @Post() @UsePipes(new ValidationPipe()) create(@Body() userDto: UserDTO) { this.userService.create(userDto); } // or @Post() create(@Body(new ValidationPipe()) userDto: UserDTO) { this.userService.create(userDto); } ``` Also can use in Global In main.ts ```typescript= app.useGlobalPipes(new ValidationPipe()); ``` ## Personal Thinking Input: dto, validation, middleware, guard, jwt, pipe database: typeorm Output: errorHandle ## Refs https://www.slideshare.net/RafaelCasusoRomate/solid-nodejs-with-typescript-jest-nestjs https://www.carloscaballero.io/tag/nestjs/ https://medium.com/@kaushiksamanta23/nest-js-tutorial-series-part-3-providers-services-dependency-injection-a093f647ce2e https://www.techiediaries.com/nestjs-tutorial-rest-api-crud/ ## Testing https://medium.com/enjoy-life-enjoy-coding/jest-jojo%E6%98%AF%E4%BD%A0-%E6%88%91%E7%9A%84%E6%9B%BF%E8%BA%AB%E8%83%BD%E5%8A%9B%E6%98%AF-mock-4de73596ea6e ## Real Time https://gabrieltanner.org/blog/nestjs-realtime-chat ## File Upload https://gabrieltanner.org/blog/nestjs-file-uploading-using-multer utils/file-uploading.utils.ts ```typescript= import { extname } from 'path'; export const imageFileFilter = (req, file, callback) => { if (!file.originalname.match(/\.(jpg|jpeg|png|gif|svg)$/)) { return callback(new Error('Only image files are allowed!'), false); } callback(null, true); }; export const editFileName = (req, file, callback) => { const name = file.originalname.split('.')[0]; const fileExtName = extname(file.originalname); const randomName = Array(4) .fill(null) .map(() => Math.round(Math.random() * 16).toString(16)) .join(''); callback(null, `${name}-${randomName}${fileExtName}`); }; ``` app.controller.ts: ```typescript= import { Controller, Get, Post, UseInterceptors, UploadedFile, Param, Res, UploadedFiles } from '@nestjs/common'; import { ApiUseTags } from '@nestjs/swagger'; import { AppService } from './app.service'; import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express'; import { diskStorage } from 'multer'; import { editFileName, imageFileFilter } from './utils/file-uploading.utils'; @ApiUseTags('base') @Controller() export class AppController { constructor(private readonly appService: AppService) {} @Get() getHello(): string { return this.appService.getHello(); } @Post() @UseInterceptors( FileInterceptor('image', { storage: diskStorage({ destination: './files', filename: editFileName, }), fileFilter: imageFileFilter, }), ) async uploadFile(@UploadedFile() file) { const response = { originalname: file.originalname, filename: file.filename, }; return response; } /** multiple images */ @Post('multiple') @UseInterceptors( FilesInterceptor('image', 20, { storage: diskStorage({ destination: './files', filename: editFileName, }), fileFilter: imageFileFilter, }), ) async uploadMultipleFiles(@UploadedFiles() files) { const response = []; files.forEach(file => { const fileResponse = { originalname: file.originalname, filename: file.filename, }; response.push(fileResponse); }); return response; } @Get(':imgpath') seeUploadedFile(@Param('imgpath') image, @Res() res) { return res.sendFile(image, { root: './files'}); } } ``` ## Mixin https://mariusschulz.com/blog/mixin-classes-in-typescript ## Testing https://leocode.com/blog/testing-node-with-jest/ https://medium.com/@jackallcock97/unit-testing-with-nestjs-and-jest-a-comprehensive-tutorial-464910f6c6ba http://www.albertgao.xyz/2017/05/24/how-to-test-expressjs-with-jest-and-supertest/ ## Learning Material https://www.twblogs.net/a/5cb8b3fbbd9eee0eff45c8de https://juejin.im/post/5d2d9532f265da1ba56b5186 ## GraphQL https://codersera.com/blog/nestjs-typeorm-graphql-dataloader-tutorial-with-typescript/ ## Debugger https://blog.entrostat.com/debugging-a-typescript-project-in-a-docker-container-using-webstorm/ ## Real Example (found this seems have cli to select different database) https://github.com/notadd/notadd https://segmentfault.com/a/1190000013568724 ---- # IT 鐵人邦 練習 https://ithelp.ithome.com.tw/users/20107195/ironman/1252 ## Request `@param` user/:id `@body` `@query` user?p=1 ## DOCKER ### DB https://hackernoon.com/docker-compose-install-postgresql-for-local-development-environment-ph293zxd log real-time ``` docker-compose logs -f ``` ---