AWS CDKでAPIGateway+Lambdaを作る

cdkの入門として、シンプルにAPIGateway+Lambdaを作っていく流れを書きます

## はじめに
* Ubuntu 18.04.4 LTS (WSL利用)
* VSCode
* aws-cdk 1.94.1
* typescript

## AWS CDKをインストール
```bash
$ npm install -g aws-cdk
$ cdk --version
```

## AWS CDKのプロジェクト作成
```bash
# プロジェクトディレクトリを作成
$ mkdir cdk-api-lambda

# 移動
$ cd cdk-api-lambda/

# プロジェクト作成
$ cdk init app --language=typescript
```

## パッケージインストール
```bash
$ npm install @aws-cdk/aws-lambda @aws-cdk/aws-apigateway
```

## lambdaコードを格納するフォルダを作成
```bash
$ mkdir lib/lambda/
```
## lambdaコードをアップロードするS3バケットを準備する
これを実施しないと **This stack uses assets, so the toolkit stack must be deployed to the environment** のエラーが発生することがあります

```bash
$ cdk bootstrap --profile xxx
 ⏳  Bootstrapping environment aws://XXXXXXXXXXX/ap-northeast-1...
CDKToolkit: creating CloudFormation changeset...
[██████████████████████████████████████████████████████████] (3/3)

 ✅  Environment aws://XXXXXXXXXXX/ap-northeast-1 bootstrapped.
```


## Lambdaの実装コードを書く

echoという名前でパラメータで受けたメッセージを単純に返却する形で作ります

```ts:/lib/lambda/echo.ts
export const handler = async (event: any = {}): Promise<any> => {
  return { statusCode: 200, body: event.queryStringParameters.msg};
};
```

## APIGatewayとLambdaを作るコードを作ります
CdkLambdaStackとCdkApiStackという2つのスタックに分けようと思いますのでファイルを分けます

```bash
# 初期に作成されたcdkapi-lambda-stack.tsを、lambdaのスタックにして
$ mv lib/cdkapi-lambda-stack.ts lib/cdk-lambda-stack.ts

# APIGatewayのスタックは新規に作ります
$ touch lib/cdk-api-stack.ts
```

```ts:/lib/cdk-lambda-stack.ts
import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda';

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

    const region = cdk.Stack.of(this).region;
    new lambda.Function(
      this,
      'echo',
      {
        code: lambda.Code.fromAsset('lib/lambda'),
        environment: {
          REGION: region,
          TZ: 'Asia/Tokyo',
        },
        functionName: 'echo',
        handler: 'echo.handler',
        runtime: lambda.Runtime.NODEJS_12_X,
        tracing: lambda.Tracing.ACTIVE,
        timeout: cdk.Duration.seconds(10),
      },
    );
  }
}
```

```ts:/lib/cdk-api-stack.ts
import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda';
import * as apigateway from '@aws-cdk/aws-apigateway';

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

    const accountId = cdk.Stack.of(this).account;
    const region = cdk.Stack.of(this).region;
    const echoFunction = lambda.Function.fromFunctionArn(
      this,
      'echo',
      `arn:aws:lambda:${region}:${accountId}:function:echo`,
    );

    const restApi = new apigateway.RestApi(this, 'RestApi', {
      restApiName: `echo-api`,
      deployOptions: {
        stageName: 'v1',
      },
    });

    const userResource = restApi.root.addResource('echo');
    userResource.addMethod(
      'GET',
      new apigateway.LambdaIntegration(echoFunction),
    );

    new lambda.CfnPermission(this, `${echoFunction}-Permission`, {
      principal: 'apigateway.amazonaws.com',
      action: 'lambda:InvokeFunction',
      functionName: echoFunction.functionName,
      sourceArn: restApi.arnForExecuteApi(
        'GET',
        '/echo',
        '*',
      ),
    });
  }
}

```

```ts:/bin/cdk-api-lambda.ts
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { CdkLambdaStack } from '../lib/cdk-lambda-stack';
import { CdkApiStack } from '../lib/cdk-api-stack';

const app = new cdk.App();
new CdkLambdaStack(app, 'CdkLambdaStack');
new CdkApiStack(app, 'CdkApiStack');
```

## デプロイの確認
作成したコードをビルドチェック

```bash
$ npm run build
```

## AWSにデプロイ
作成したコードをAWSにデプロイします

```bash
# $ cdk deploy --all --profile myself
$ cdk deploy CdkLambdaStack --profile myself
$ cdk deploy CdkApiStack --profile myself
CdkApiStack: deploying...
CdkApiStack: creating CloudFormation changeset...
[██████████████████████████████████████████████████████████] (10/10)

 ✅  CdkApiStack

Outputs:
CdkApiStack.RestApiEndpointXXXXXXX = https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/v1/

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxxx:stack/CdkApiStack/xxxxxxxxxxxxxxxxxxxxxxx
```

### CloudFormationで確認
スタックが正常に完了しています

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1219565/44916be8-aac6-606d-a286-f1f4471ab707.png)

### APIGatewayで確認
作成されています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1219565/f8bac297-1ca6-f56b-9563-667012348089.png)

### APIを呼び出して確認
```bash
$ curl https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/v1/echo?msg=hoge
hoge
```

応答もOKです

## AWSから削除
デプロイしたものを削除します

```bash
$ cdk destroy CdkApiStack --profile xxx
$ cdk destroy CdkLambdaStack --profile xxx
# $ cdk destroy --all --profile xxx
```