[備忘録]AWS CDKでAPI Gateway, Lambda を構築

AWS CDKを学習していく過程を記録する。

# はじめに
[[備忘録] AWS CDK プロジェクトの作成](https://qiita.com/m-chan/items/011a93e40f26230df135)にて、CDK プロフェクトの作成とデプロイまでできたので、今回はAWS CDKでAPI GatewayおよびLambdaを作成してみる。  
また、API GatewayをトリガーにLambdaを実行することで、LambdaがRestAPIとして外部ネットワークから利用可能になることを確認する。

#### Lambdaとは
> AWS Lambda は、サーバーをプロビジョニングまたは管理せずにコードを実行できるようにするコンピューティングサービスです。

つまり、「サーバレスにインターネットを通じてプログラムを実行することができるサービス（Faas:Function as a Service）」である。

#### API Gatewayとは
公式サイトでは
> Amazon API Gateway は、あらゆる規模の REST、HTTP、および WebSocket API を作成、公開、維持、モニタリング、およびセキュア化するための AWS のサービスです。  

と説明されている。
要するに「APIを作成、管理するための AWS のサービス」である。  

# 実施したこと
1. Lambdaの作成
1. API Gatewayの作成

### 1. Lambdaの作成
#### Lambda の処理内容の作成
lambdaフォルダと、実際に Lambda の処理内容を書いていく app-lambda.ts ファイルを作成した。
```
├── lambda
     └── app-lambda.ts	
```
```app-lambda.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';

export const handler = async (
  event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {

  const RESPONSE_HEADERS = {
    "Content-Type": "application/json",
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Headers": "Content-Type,Authorization,access-token",
  };

  return {
    statusCode: 200,
    headers: RESPONSE_HEADERS,
    body: "成功",
  };
};
```
今回は単に "成功" のメッセージを返す Lambda とした。
また、Lambda の型定義のために下記コマンドより　@types/aws-lambda をインストールした。
```
% npm i --save-dev @types/aws-lambda
```

#### Lambda の作成
app-stack.ts に、Lambda 用の IAM Role と Lambda を作成するためのコードを追加。

``` app-stack.ts
import { Stack, StackProps, Duration } from 'aws-cdk-lib';
import { Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import { Construct } from 'constructs';

export class AppStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    // iam role for lambda
    const iamRoleForAppLambda = new Role(this, 'IamRoleForAppLambda', {
      roleName: 'app-lambda-role',
      assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
    });

    // lambda
    const lambda = new NodejsFunction(this, 'AppLambda', {
      functionName: 'app-lambda',
      entry: 'lambda/app-lambda.ts',
      runtime: Runtime.NODEJS_16_X,
      timeout: Duration.seconds(30),
      role: iamRoleForAppLambda,
      memorySize: 128
    });
  }
}
```

#### 確認
デプロイ後コンソールから Lambda が作成されていることを確認できた。
※デプロイ時エラーが発生したが、解決方法については[[備忘録] Error: spawnSync docker ENOENTの解消](https://qiita.com/m-chan/items/1a3f4d53d3470c32405c)にまとめた。
![スクショ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280374/2acc4600-7f20-b792-727c-fbea438a3fd5.png)

また、テストを実行したところ、期待通り"成功"のメッセージも返ってきた。
![スクリーンショット 2023-08-06 17.51.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280374/8a6e7088-b0ef-1a13-7c02-d2025717ad1f.png)

### 2. API Gatewayの作成

#### API Gatewayの作成
app-stack.ts に、API Gatewayおよび API Key を作成するコードを追記し、それらを使用量プランで紐づける。  

``` app-stack.ts
import { LambdaIntegration, RestApi } from 'aws-cdk-lib/aws-apigateway';

// api gateway
const api = new RestApi(this, 'AppApigateway', {
  restApiName: 'app-apigateway'
});

// api key
const apiKey = api.addApiKey('AppApikey', {
  apiKeyName: 'app-apikey'
});

// usage plan
const usagePlan = api.addUsagePlan('AppUsageplan');
usagePlan.addApiKey(apiKey);
usagePlan.addApiStage({ stage: api.deploymentStage });
```

さらに、作成した API Gateway にリソースを追加、lambda をアタッチした。
``` app-stack.ts
const get = api.root.addResource('get');
const lambdaIntegration = new LambdaIntegration(lambda);
get.addMethod('GET',lambdaIntegration,{
  apiKeyRequired: true,
});
```

#### 確認
デプロイ後コンソールから API Gateway が作成されていることを確認できた。
![スクリーンショット 2023-08-11 13.23.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280374/8e3cba03-c9f8-2e04-a3da-bc83073d545d.png)

postmanを用いてAPIにリクエストを送信したところ、期待通り"成功"のメッセージも返ってきた。
![スクリーンショット 2023-08-11 13.25.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280374/8f346374-ebb7-0485-0605-8ae236589418.png)

また、今回 API Key を必須としたため、Authorization　のタブから API Key を設定している。この設定がないときは、Forbidden が返ってくることも確認できた。
```
{
    "message": "Forbidden"
}
```

# 最後に
AWS CDKでAPI GatewayおよびLambdaを作成し、API を叩くと期待通りの結果が返ってくることを確認できた。  
次は、RDS、RDS Proxy を構築し、Lambda から mysql に接続できることを確認したい。


参考にした文献を記す。
- https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/welcome.html  
- https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/welcome.html  
- https://www.wafcharm.com/jp/blog/aws-lambda-for-beginners/
- https://zenn.dev/t_morooka/articles/2951743c240063
- https://dev.classmethod.jp/articles/apig-and-lambda-best-stack-configuration-with-aws-cdk/
- https://www.npmjs.com/package/@types/aws-lambda