NestJSをAPI Gateway + AWS Lambdaで構成する（その８） AWSへのデプロイ

#
- [その１ NestJSやLambdaでのnode.jsの話し](https://qiita.com/ssugimoto/items/988320d34022555401fb)
- [その２ NestJSのサンプルを動かす、Hello World.](https://qiita.com/ssugimoto/items/557e7a7cce5111bf5de7)
- [その３ コントローラーを追加する](https://qiita.com/ssugimoto/items/fcfbc9a176bcf41d7b45)
- [その４ DTOを使う](https://qiita.com/ssugimoto/items/613b8ab54bd6f8de043c)
- [その５ CRUD generatorを使い modules,controller, service, dto を生成する](https://qiita.com/ssugimoto/items/64a9c3cd5cb33e06f4c9)
- [その６ インターセプタ](https://qiita.com/ssugimoto/items/dce542a78fdfbaf1e434)
- [その７ ORMでRDBデータベースを扱う](https://qiita.com/ssugimoto/items/45e23d1b2879b083267c)
- その８ NestJSをAPI Gateway＋AWS Lambdaとしてデプロイ

---
本題、ここまでその１～７では AWS へのデプロイを一切していない（ふれていない）ので、AWS APIGateway + AWS Lambdaとして NestJSをデプロイします

## 制約や前提
### AWS関連のリソース
- その７まででAWS上でAmazon RDSを使うと、VPC、セキュリティグループ、RDS、VPC内Lambda 等が出てきて ややこしくなるのでRDS無しです。
- NestJSがAPI Gateway + AWS Lambdaとして動くかどうかという観点です

### データベースは使わずNestJSのデプロイを試すためPrisma関連はコメントにする
- NestJSでの初期化処理でPrismaServiceがDB接続をしにいくためコメントにする
- 遅延初期化の方法は調べていませんが不明のためコメントで回避します
    - 遅延初期化：必要になったときに始めてPrismaServiceでDB接続する
    - `implements OnModuleInit` や `async onModuleInit()` で何らか遅延初期化できると嬉しいのだけど・・・

### webpack等は使わない、Lambda Layerも使わない
- ひとまず、手軽にできるdistやnode_modulesを使う
- tsconfig.json と serverless.ymlでデプロイするアップロード用のコードzipを調整する


## 1. serverless.ymlを変更
- distディレクトリをLambda用のソースのzipファイルとして利用、node_modulesをzipに含める
- tsファイルは不要だけどzip内で一緒にしておく
```serverless.yml
service: serverless-nest-example
frameworkVersion: "3"

plugins:
  - serverless-plugin-typescript
  - serverless-plugin-optimize
  - serverless-offline

provider:
  name: aws
  runtime: nodejs18.x
  region: us-east-1
  
package:
  individually: true
  include:
    - dist/**
  # exclude:
  #   - "*"
  #   - "**/node_modules/aws-sdk/**" # included on Lambda.
  #   - "!package.json"
functions:
  main:
    handler: src/main.handler
    events:
      - http:
        method: ANY
        path: /
      - http:
          method: ANY
          path: /{proxy+}
```

## 2. AWSへAPI GatewayとLabmda関数をデプロイする
- AWSへデプロイ
```
cd aws-node-typescript-nest
sls deploy
```
- `sls deploy`実行結果例
```
$ sls deploy
Running "serverless" from node_modules

Warning: Invalid configuration encountered
  at 'functions.main.events.0': unsupported function event

Learn more about configuration validation here: http://slss.io/configuration-validation

Deploying serverless-nest-example to stage dev (us-east-1)
Compiling with Typescript...
Using local tsconfig.json - tsconfig.json
test/app.e2e-spec.ts (19,12): This expression is not callable.
  Type 'typeof supertest' has no call signatures.
Typescript compiled.

✔ Service deployed to stack serverless-nest-example-dev (136s)

endpoint: ANY - https://xxx.execute-api.us-east-1.amazonaws.com/dev/{proxy+}
functions:
  main: serverless-nest-example-dev-main (69 MB)

2 deprecations found: run 'serverless doctor' for more details

Monitor all your API routes with Serverless Console: run "serverless --console"
```
#### API Gatewayのマネジメントコンソール
- API Gatewayのリソース
![1-api-gateway.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/5470a183-105f-42bf-694b-cad5c15aaecb.png)
- API GatewayでのAPIエンドポイント
![2-api-gateway.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/1e1baf80-4475-04de-6e46-856322f4769b.png)


#### AWS Lambdaのマネジメントコンソール
- 作成された Lambda function
![3-aws-lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/8edf67d9-ad6b-6d2d-5ff0-ff669f205790.png)

- 作成された Lambda functionのトリガー（インプット）はAPI Gateway
![4-aws-lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/d6845ac7-a2ea-d923-f2b8-d922576b442a.png)

- Lambda は メモリ使用量 1024MB、slsのV3あたりからのデフォルトhttps://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/#general-function-settings `# Default memory size for functions (default: 1024MB)`）
![7-lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/32fae23b-4f7b-7485-467c-7d0a27fa1e76.png)

## 3. APIを実行
- API GatewayでのLambdaは１つのみ、Lambda function 1つで 複数のエンドポイントを処理する

### APIとしての `/hello` を curlから実行してみる
```
$ curl curl https://xxx.execute-api.us-east-1.amazonaws.com/dev/hello
Hello World! my
```

- CloudWathc LogsでのLambda functionのログ
![5-lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/4f551fa5-cb1c-9371-da28-39a8225f01bd.png)
- 緑が初回（NestJSの初期化で時間かかっている、コールドスタートかな？）
- 青が２回目（速くなった、Lambdaはウォームスタートになっているかな？）


### APIとしての `/cats/{id}` を curlから実行してみる
```
curl https://xxx.execute-api.us-east-1.amazonaws.com/dev/cats/123
This action returns a #123 cat
```
 
- CloudWathc LogsでのLambda functionのログ

![6-lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/8d60589d-d7d2-09a8-1a6a-dd9154394ec4.png)

- インターセプターのログが表示されている
- パラメーターとして受けた {id} の文字列が表示されている


## 4. AWSから API Gateway 、AWS Lambdaを消す
- まっさらに消えるので注意
```
sls remove
```


## 感想
- １つのLambda function として 複数のAPI・エンドポイントに対応できるのはWebフレームワークのメリット
- APIごとに同じLambda functionなのでコールドスタートも減る
- NestJSのinterceptor、module、Serviceが増えると初期化時間はだんだん長くなりそうに思われる
- 同じLambdaになるので、CloudWatch Logsでのモニタリング・監視等する場合はキーワード等の調整をしていく、ログ出力の改善が必要と思われる
- アップロードするファイルはnode_modules等肥大化するので小さくしていきたい
- NestJSをECS Fargateとして動かすこともやってみたい、ただVPC等が出てくるため、ひと手間かかる、Serverless FrameworkでVPCやサブネット、セキュリティグループ等は定義せず、Terraform等を使いところである