AWS CDK with TypeScrip API Gateway & Lambda (Part1)

## 環境情報
* 作業環境はAWS Cloud9を利用
* cdk versionは2.63.2

## 作成イメージ
AWS CDKを利用してAmazon API Gateway経由でコールできるLambdaサービスのデモを作成していきたいと思います。作成するデモイメージは以下です。
![hello-arch.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/b7005fea-515a-a3d7-7e41-a02a8b342742.png)
では早速手を動かしていきましょう。

## CDK新規プロジェクトの作成
今回の作業環境はAWS Cloud9を利用しています。タイトルにもある通り、今回はTypeScriptでプロジェクトを作成していきます。
まずは今回使用していくプロジェクト用のディレクトリを作成します。
```
mkdir cdk-workshop && cd cdk-workshop
```
TypeScriptのCDKプロジェクトを作成します。（ちょっと時間かかります）
```
cdk init sample-app --language typescript
```
こんな感じで、便利なcdkコマンドが表示されているかと思います。後にこれらを利用していきます。
```
## Useful commands

* `cdk deploy`      deploy this stack to your default AWS account/region
* `cdk diff`        compare deployed stack with current state
* `cdk synth`       emits the synthesized CloudFormation template
```
下図のようなプロジェクトディレクトリが作成されているかと思います。
![スクリーンショット 2023-02-07 15.25.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/17a6e174-0273-be2d-3144-70f55a0b30cb.png)
補足しておくと、
* `lib/cdk-workshop-stack.ts`: CDKアプリケショーンのメインスタックを定義するファイルです。こちらのファイルをメインに編集していきます。
* `bin/cdk-workshop.ts`: CDKアプリケーションのエントリーポイントになります。`lib/cdk-workshop-stack.ts`で定義されたスタックを読み込みます。
* `cdk.json`: toolkitに対してアプリのrunの方法を定義しています。今回の場合は`npx ts-node bin/cdk-workshop.ts`のように記述されています。

## Lambda関数・API Gatewayの作成
### 不要コードのクリーンアップと初回のデプロイ 
まずは不要である既存のコードをいくつかクリーンアップするところからです。
`lib/cdk-workshop-stack.ts`を開いて、以下の様にクリーンアップします。SQS QueueやSNS topicは今回のデモでは不要なためです。
```typescript:lib/cdk-workshop-stack.ts
import * as cdk from 'aws-cdk-lib';

export class CdkWorkshopStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // nothing here!
  }
}
```
中身は何もありませんが、CDKで記述した中身からAWS CloudFormationのテンプレートを作成していきます（**synthesize**する）。CDKを利用した場合も、AWS環境へデプロイする際はCloudFormationテンプレートのカタチでデプロイする、ということです。
```
cdk synth
```
CloudFormationテンプレートが`checkout`フォルダに作成されました。

続いて、初回のデプロイ時のみ**bootstrap stack**というスタックをインストールできます。こちらのスタックには、CDK Toolkitで利用されるリソース（例えばCloudFormationテンプレートを保持するS3バケットなど）が含まれています。以下のコマンドです。
```
cdk bootstrap
```
それではデプロイしていきます！
```
cdk deploy
```
デプロイ成功後のCloudFormationのコンソールを見ると、作成したスタックが存在しているのがわかりますね！（`cdk synth`で作成したテンプレートが適用されたという意味です。）
![スクリーンショット 2023-02-07 16.09.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/676cc71d-ada8-1878-081a-73197e0666d6.png)

### Hello Lambda関数の作成
Lambda関数のコードを記述していきましょう。以下を実施していきます。
1. `lambda`ディレクトリーをbinやlibディレクトリーと同じ階層に作成。
1. `.gitignore`ファイルが存在している場合は、ファイル内に`!lambda/*.js`を追記。
1. `lambda/hello.js`ファイルを作成して、以下のコードを記述します。
```javascript:lambda/hello.js
exports.handler = async function(event) {
  console.log("request:", JSON.stringify(event, undefined, 2));
  return {
    statusCode: 200,
    headers: { "Content-Type": "text/plain" },
    body: `Hello, CDK! You've hit ${event.path}\n`
  };
};
```
### 作成したLambda関数をスタックに追加
先ほど作成したlambda関数をスタックに追加します。Lambdaスタックの追加にはLambda用のCDK Libraryも必要なのでimportします。
```typescript:lib/cdk-workshop-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';

export class CdkWorkshopStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // defines an AWS Lambda resource
    const hello = new lambda.Function(this, 'HelloHandler', {
      runtime: lambda.Runtime.NODEJS_16_X,
      code: lambda.Code.fromAsset('lambda'),
      handler: 'hello.handler'
    });
  }
}
```
上記コードについて少し補足すると、
* NodeJS(version16)ランタイムを利用。
* Lambda関数のhandlerはlambdaディレクトリから読むこまれるように記述。
* handlerの名前も作成した関数内のhandlerと一致させている。
のようにしています。

デプロイの前にコードの差分を見てみましょう。差分が確認できると思います。
```
cdk diff
```
### デプロイ
デプロイします。
```
cdk deploy
```
deploy後にコンソールを見てみると、`hello.js`のLambda関数ができていることと、S3バケット内にも`asset`フォルダが作成されていることが分かると思います。

### テスト
作成されたLambda関数をテストしてみます。Lambdaのコンソールから"Amazon API Gateway AWS Proxy"テンプレートを使ったテストを作成して実行してみます。
![スクリーンショット 2023-02-08 17.50.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/1a9138a4-2fab-d4d7-636e-408f70feff04.png)
良い感じに返ってきました。
![スクリーンショット 2023-02-08 17.52.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/acdc5537-9881-f3a6-b971-6c4f9ec84a4a.png)

### API Gatewayの作成
次にAPI Gatewayを作成していきます。API Gatewayはインターネット上にパブリックエンドポイントを公開してくれるサービスで、API GatewayとLambdaを繋げることでブラウザなどからリクエストを受け取れるようになります。
以下のように記述します。
```typescript:lib/cdk-workshop-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigw from 'aws-cdk-lib/aws-apigateway';

export class CdkWorkshopStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // defines an AWS Lambda resource
    const hello = new lambda.Function(this, 'HelloHandler', {
      runtime: lambda.Runtime.NODEJS_16_X,
      code: lambda.Code.fromAsset('lambda'),
      handler: 'hello.handler'
    });
    
    // defines an API Gateway REST API resource backed by "hello" function.
    new apigw.LambdaRestApi(this, 'Endpoint', {
      handler: hello
    });
  }
}
```
スタックの差分を見てみましょう。
```
cdk diff
```
一気に沢山のリソースが作成されちるのがわかりますね。CDKの利便性を感じます。
```
Resources
[+] AWS::ApiGateway::RestApi Endpoint EndpointEEF1FD8F 
[+] AWS::ApiGateway::Deployment Endpoint/Deployment EndpointDeployment318525DA5f8cdfe532107839d82cbce31f859259 
[+] AWS::ApiGateway::Stage Endpoint/DeploymentStage.prod EndpointDeploymentStageprodB78BEEA0 
[+] AWS::ApiGateway::Resource Endpoint/Default/{proxy+} Endpointproxy39E2174E 
[+] AWS::Lambda::Permission Endpoint/Default/{proxy+}/ANY/ApiPermission.CdkWorkshopStackEndpoint018E8349.ANY..{proxy+} EndpointproxyANYApiPermissionCdkWorkshopStackEndpoint018E8349ANYproxy747DCA52 
[+] AWS::Lambda::Permission Endpoint/Default/{proxy+}/ANY/ApiPermission.Test.CdkWorkshopStackEndpoint018E8349.ANY..{proxy+} EndpointproxyANYApiPermissionTestCdkWorkshopStackEndpoint018E8349ANYproxy41939001 
[+] AWS::ApiGateway::Method Endpoint/Default/{proxy+}/ANY EndpointproxyANYC09721C5 
[+] AWS::Lambda::Permission Endpoint/Default/ANY/ApiPermission.CdkWorkshopStackEndpoint018E8349.ANY.. EndpointANYApiPermissionCdkWorkshopStackEndpoint018E8349ANYE84BEB04 
[+] AWS::Lambda::Permission Endpoint/Default/ANY/ApiPermission.Test.CdkWorkshopStackEndpoint018E8349.ANY.. EndpointANYApiPermissionTestCdkWorkshopStackEndpoint018E8349ANYB6CC1B64 
[+] AWS::ApiGateway::Method Endpoint/Default/ANY EndpointANY485C938B 
```
デプロイします。
```
cdk deploy
```
Outputとして以下の様なURLエンドポイントが表示されると思います。
```
Outputs:
CdkWorkshopStack.Endpoint8024A810 = https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/
```
crulコマンドでこのエンドポイントを叩いてあげれば、Lambda関数からレスポンスが返ってくるのがわかりますね！ブラウザからも試してみてください。
```
$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/
Hello, CDK! You've hit /
```

## まとめ
今回はCDKを用いて簡単なバックエンドアプリをAPI Gateway + Lambdaの構成で作成してみました。
コンソールでポチポチ構成する場合やCloudFormationを記述する場合と比べて、時間的にはかなり短縮して作成完了ができるような感覚ではなかったでしょうか。今後もCDKシリーズを公開していきたいと思います。

[Part2](https://qiita.com/kennyQiita/items/7d684a0e9792c8e808a7)として、Hello Lambdaにヒットした回数をカウントするHit Counter LambdaとDynamoDBをCDKで作成した内容を[こちら](https://qiita.com/kennyQiita/items/7d684a0e9792c8e808a7)に記載しています。

最後に、今回作成したアプリを削除したい場合は以下のコマンドで綺麗サッパリクリーンアップされます。
```
cdk destroy
```

## 参考サイト
https://cdkworkshop.com/20-typescript.html

以上。