NestJSをLambdaで使いたいんや！① ~ 環境構築編~


# NestJSを使ったAPI作成備忘録①

こんにちは。
業務でNestJSを使用し実装するため、勉強も兼ねて自作APIを作成しようと思います。
先人たちはたくさんいるので、ググれば情報はたくさん出てきますが備忘録も兼ねてブログに記載します。
あわよくばdynamodbの設計思想にも触れられれば、、、
（当方、ほぼRDSMSしか触ったことがなく、NoSQLには苦手意識がやばいため。）
今回は序章。まだまだ先は長い。

## 自己紹介
エンジニア歴だけが重ねてしまったポンコツエンジニア。
エンジニアレベルはそこまで高くないため、同じようなポンコツエンジニアたちの希望の記事にしたい。

## 最終目標
- NestJSをLambda + API Gatewayで動かす
- DynamoDB を使用しデータ連携を行う
- Cognito を使用しユーザ認証を行う
- Open APIで仕様書の作成
- テストコードの作成

## 今回やること
1. 環境構築
1. プロジェクト作成
1. データをGET / Post 実行

## バージョン

- NodeJS
18.19.0

## 環境構築
まずは環境構築から、レッツゴ。

#### NestJSをグローバルにインストール

```bash
**********@Mac-mini ~ % npm install -g @nestjs/cli
```

#### プロジェクト作成

```bash
# プロジェクト作成
**********@Mac-mini ~ % nest new nest-study-api 

# プロジェクトに移動
**********@Mac-mini ~ % cd nest-study-api

```
## 簡易的なAPIを作成する

今回はnewsの一覧を取得するモジュールと登録するモジュールを作成します。

### newsモジュール作成

```bash
# _モジュールの作成
**********@Mac-mini nest-stusy-api % nest generate resource news 
? What transport layer do you use? REST API
? Would you like to generate CRUD entry points? Yes

# モジュール確認
**********@Mac-mini nest-stusy-api % ll src/news 
total 40
drwxr-xr-x  4 **********  staff  128  1 13 11:59 dto                        -- リクエストの型定義ファイルの置き場所
drwxr-xr-x  3 **********  staff   96  1 13 11:59 entities                   -- DBのテーブル構造定義ファイルの置き場所
-rw-r--r--  1 **********  staff  566  1 13 11:59 news.controller.spec.ts   -- controllerのテストファイル
-rw-r--r--  1 **********  staff  894  1 13 11:59 news.controller.ts        -- HTTPリクエストを処理するファイル
-rw-r--r--  1 **********  staff  248  1 13 11:59 news.module.ts            -- モジュールファイル
-rw-r--r--  1 **********  staff  453  1 13 11:59 news.service.spec.ts      -- serviceのテストファイル
-rw-r--r--  1 **********  staff  609  1 13 11:59 news.service.ts           -- ビジネスロジックファイル
```

※詳細な説明は本記事では省略しますので気になる方は別途調べてみてください。

### 不必要なディレクトリの削除
今回はDynamoDBを使用する想定ですので、entitiesディレクトリは必要ないので削除します。

```bash
**********@Mac-mini nest-stusy-api % rm -rf ./src/news/entities 
```

### Get / Postメソッドの作成

```typescript:src/news/news.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { NewsService } from './news.service';
import { CreateNewsDto } from './dto/create-news.dto';

@Controller('news')
export class NewsController {
  constructor(private readonly newsService: NewsService) {}

  @Get()
  findAll(){
    return this.newsService.findAll();
  }

  @Post()
  create(@Body() createNews: CreateNewsDto) {
    return this.newsService.create(createNews);
  }

}
```

```typescript:src/news/news.service.ts
import { Injectable } from '@nestjs/common';
import { CreateNewsDto } from './dto/create-news.dto';

@Injectable()
export class NewsService {
  news: CreateNewsDto[] = [];
  create(news: CreateNewsDto) {
    this.news.push(news);
  }

  findAll() {
    return this.news;
  }

}
```

- Postリクエストの場合、リクエストの値を配列に格納する。
- Getリクエストの場合、配列のすべての値を返却する。

処理ができました。

### DTOの作成
ここではリクエストデータの定義をします。
タイトル、本文、入力日付のデータを受け取る定義を記入していきます。

ここではバリテーションを定義することもできるので、
バリデーションパイプ というNestJSのパッケージを使用しバリテーションチェックも同時にやります。

※ DTO ・・・データトランスファーオブジェクト

#### パッケージのインストール

```bash
**********@Mac-mini nest-stusy-api % npm i --save class-validator class-transformer
```

#### バリデーションパイプの提供
@Bodyに対してValidationPipeを提供します。

```typescript:src/news/news.controller.ts
import { Controller, Get, Post, Body, ValidationPipe } from '@nestjs/common';

...

  @Post()
  create(@Body(ValidationPipe) createNews: CreateNewsDto) {
    return this.newsService.create(createNews);
  }

...

```

各項目に対してデコレータを設定し、バリデーションチェックを行います。
```typescript:create-news.dto.ts
import { IsNotEmpty, IsString, Length } from "class-validator";

export class CreateNewsDto {
    @IsString()                       -- stringじゃなきゃダメ
    @IsNotEmpty()                     -- 必須項目だよ
    @Length(0,20)                     -- 0 ~ 20文字以内にしてね
    readonly title!: string;
    @IsString()
    @IsNotEmpty()
    readonly text!: string;
    @IsString()
    @IsNotEmpty()
    @Length(10)
    // YYYY-MM-DD
    readonly date!: string;
}
```

以上で実装は完了です。
実際にPost / Get してみて動作確認を行います。

### Get / Postメソッドの動作確認
Postmanを使用する方法もありますが、今回はCurlで確認してみます。

```bash:まずはローカルサーバを起動
**********@Mac-mini nest-stusy-api % npm start dev
```

```bash:Postしてみる
# Post でデータを格納してみる
curl -X POST \
  http://localhost:3000/news \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "test-title1",
    "test": "test-text1111",
    "date": "2023-01-13"
}'

curl -X POST \
  http://localhost:3000/news \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "test-title2",
    "test": "test-text2222",
    "date": "2023-01-14"
}'
```

特に戻り値はないので実行後何も返却されませんが正しくPostされているはずです。
Getで確認してみます。

```:ブラウザで確認
http://localhost:3000/news

[{"title":"test-title1","test":"test-text1111","date":"2023-01-13"},{"title":"test-title2","test":"test-text2222","date":"2023-01-15"}]
```

正しく取得できていることが確認できました。
以上で簡易的なAPIの作成が完了です。

ただし、これではサーバを停止するとデータも消えてしまうので、
次回はDynamoDBを使用してデータの連携を行います。