---
# System prepended metadata

title: '[LEARNING] Nest.js'

---

# [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
```

---