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