NestJSをはじめてみる ~#1導入編~

# はじめに
30代未経験からエンジニアを目指して勉強中のYNと申します。
TypeScriptを勉強しているなか、NestJSというフレームワークにたどり着きました。これから長い付き合いになりそうです。
学んだことをメモとして書きます。

# プロジェクトを始める
```
npm i -g @nestjs/cli  #グローバルインストール
nest new <project-name> #プロジェクト作成
yarn start:dev #サーバー起動

// もしグローバルインストールをしない場合↓
// yarn add -D @nestjs/cli @nestjs/schematics
// npx nest new <project-name>
```
# 基本構造
プロジェクトを作成すると、`module` `controller` `service`が作られる。
それそれの役割は[公式ドキュメント](https://docs.nestjs.com/controllers)が分かりやすいが、下図が自分の理解。

* `module`: 一つのAPI。リクエストを受け取る。
* `controller`: routerとして機能。moduleに届いたリクエストを各エンドポイントごとに処理する
* `service`: 実際の処理を記述する
![スクリーンショット 2020-11-20 16.30.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/32982b1c-8a3c-a16e-dfc8-e9d4af98d70d.png)

`$nest new <project-name>`直後の構造はこんな感じ
![スクリーンショット 2020-11-20 17.20.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/cdca75a0-c8fa-d56b-76bc-b27243c5a90f.png)


# CLIコマンド
`module` `controller` `service`などのコンポーネントの雛形はCLIで作ることができる。[公式ドキュメント](https://docs.nestjs.com/cli/usages)参照。

```
nest g <schematic> <name> [options]
```

# Pipe
`Pipe`はリクエストの内容を`validate`したり`transform`したりできる。`module`・`controller`・`service`に届く前のいずれかの場所に実装できる。
![スクリーンショット 2020-11-20 16.52.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/7b23489f-b000-e397-0f53-96726d6da268.png)
![スクリーンショット 2020-11-20 17.16.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/530dd593-bdd0-ebd7-76d9-8b142bf7c57d.png)



# あたらしくControllerを実装する
こんな感じのAPIを実装してみます。
![スクリーンショット 2020-11-20 17.51.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/b07a237e-e440-1e84-8e22-f73fdce7c428.png)


## CLIでcontrollerとserviceを追加
自動的に`src/cats`フォルダにcontrollerとserviceを作ってくれる。

```
nest g controller cats 
nest g service cats 
```
また、`app.module.ts`にcontrollerが追加されていることがわかる。

```src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';

@Module({
  imports: [],
  controllers: [AppController, CatsController], //ここにcontrollerが自動で追加されている
  providers: [AppService, CatsService],
})
export class AppModule {}
```
## Cat interfaceの定義
データとして保持するためのCatの型を定義する。

```src/cats/interfaces/cat.interface.ts
export interface Cat {
  name: string;
  age: number;
  breed: string;
}
```

## Cat DTOの定義
リクエストbodyで送られてくる情報を規定する。
interfaceではなく、class表現をつかうことで、validationのためのデコレーターを使うことができる。
これと前述のPipeを組み合わせて、リクエストのvalidationが可能となる。

```src/cats/dto/create-cat.dto.ts
import { IsString, IsInt } from 'class-validator';

export class CreateCatDto {
  @IsString()
  name: string;

  @IsInt()
  age: number;

  @IsString()
  breed: string;
}
```

## controllerの記述
controllerでvalidationを行う場合、`@UsePipes(new ValidationPipe())`で行う。
`ValidationPipe`の中身については[こちらを参照](https://qiita.com/mana-vv/items/463a0e859e71a37ee3e4#class-validator)させていただくと分かりやすい。

```src/cats/cats.controller.ts
import {
  Controller,
  Get,
  Post,
  Body,
  ValidationPipe,
  UsePipes,
} from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';

@Controller('cats')
@UsePipes(new ValidationPipe({ transform: true })) //　ここでvalidationをする
export class CatsController {
  constructor(private catsService: CatsService) {}

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto); // serviceを実行
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll(); // serviceを実行
  }
}
```
## serviceの記述
```src/cats/cats.service.ts
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
  }

  findAll(): Cat[] {
    return this.cats;
  }
}
```
## 動作確認
postした後にgetすると登録したcatsが返ってくることがわかる。
![スクリーンショット 2020-11-20 18.15.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/2c961255-6556-4583-3a44-60a998356df3.png)
![スクリーンショット 2020-11-20 18.16.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/a99b69e6-6bcf-ad65-01f3-9ed42531c536.png)
ageにnumber以外をpostするとちゃんとエラーが返ってくる
![スクリーンショット 2020-11-21 7.45.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/29a2d723-9739-7744-73b0-3d266fee5b23.png)

# 参考にさせていただいたもの
https://youtu.be/HQ3tjl_Sh9s
https://youtu.be/F_oOtaxb0L8
https://qiita.com/taqm/items/4bfd26dfa1f9610128bc