Step Functionsを起動するためのAPI GatewayをCDK L2コンストラクトで実装した

# 0. 記事を書いたきっかけ

Step FunctionsをAPI Gateway経由で起動したいな〜と思って調べたら、公式にやり方は載っていたのですがそれをCDKで実装するのにちょっと時間がかかったので未来の自分に向けて記事を書きました。

https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/tutorial-api-gateway.html

# 1. 実装したもの


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/adfd3e9b-1150-fea9-ad48-4057e2f846c8.png)


全体像はこんな感じで、この記事では赤矢印の部分を実現するためのAPI Gatewayの説明をします。


# 2. 実際のコード(完成品）
constructs/api_gateway.ts
``` typescript
import * as cdk from 'aws-cdk-lib';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';

// API Gatewayのコンストラクトの引数として、StateMacineのARNを受け取るようにする。
interface ApiGatewayStepFunctionsProps extends cdk.StackProps {
  stateMachineArn: string;
}

// コンストラクトの定義
export class ApiGatewayToStepFunctions extends Construct {
  constructor(scope: Construct, id: string, props: ApiGatewayStepFunctionsProps) {
    super(scope, id);

    // API Gatewayそのものを作成
    const api = new apigateway.RestApi(this, 'StepFunctions API', {
      restApiName: 'StepFunctions API',
      description: 'API Gateway to trigger Step Functions',
    });

    // API Gatewayのリソース。リソースとはURLの一部を表す。つまり/api/executeが作成される
    const executeResource = api.root.addResource('execute');

    // API Gatewayが使用するためのIAMロールを作成。ポリシーは後で付与。
    const apiGatewayRole = new iam.Role(this, 'ApiGatewayStepFunctionsRole', {
      assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'),
    });

    // 上のapiGatewayRoleに必要なポリシーを付与している。
    // 引数として受け取ったステップファンクションを実行するためのポリシーを付与
    apiGatewayRole.addToPolicy(new iam.PolicyStatement({
      actions: ['states:StartExecution'],
      resources: [props.stateMachineArn],
    }));

    // executeエンドポイントにリクエストが送られてきたときの処理を定義
    // AwsIntegrationはAPI GatewayのリクエストをAWSのサービスに転送するための設定。
    // つまり、API Gatewayからステップファンクションにリクエストを転送する設定を行っている
    const integration = new apigateway.AwsIntegration({
      service: 'states',
      action: 'StartExecution',
      integrationHttpMethod: 'POST',
      options: {
        credentialsRole: apiGatewayRole,
        integrationResponses: [{
          statusCode: '200',
        }],
        requestTemplates: { //API Gatewayにリクエストが送られたときのリクエストボディ全体をinputとしてステップファンクションに渡す
          'application/json': `{
            "input": "$util.escapeJavaScript($input.body)",
            "stateMachineArn": "${props.stateMachineArn}"
          }`
        },
      },
    });

    // /executeリソースにPOSTメソッドが使えるように設定。そのPOSTメソッドの処理をintegrationで定義
    executeResource.addMethod('POST', integration, {
      methodResponses: [{
        statusCode: '200',
      }],
    });
  }
}
```

stacks/infra_stack.ts
```typescript
new ApiGatewayToStepFunctions(this, 'ApiGateway', {
      stateMachineArn: stateMachineArn //parameter.tsに具体的な値は外だし
    });
```

# 3. 理解するべき概念
### IAMによる最小権限の付与
省略！多分(未来の自分も)コメント読めばわかるはずだと思います。

### API Gatewayの基本的な構築手順
- APIの作成 (const apiの部分）
    - HTTP、WebSocket、RESTのどれかから作る。今回はREST APIを作成している

<br>

- リソースの作成 (const executeResourceの部分)
    - リソースとはAPIのパス。つまりエンドポイント(/api/execute)


<br>

- メソッドの作成 (executeResource.addMethodの部分)
    - リソースに対してどのHTTPメソッドを利用するのかを設定する
    - 第2引数で統合タイプを入れる。いつもはLambdaとかを追加するためにLambdaIntegrationなどを入れているが、今回はそこにAwsIntegrationを入れている（AWSサービス統合の設定）。その「AWSサービスとの統合の設定」の概念がAwsIntegrationというクラスで扱われているという認識で良さそう

### AwsIntegration

「AWSサービスとの統合の設定」の概念を扱うクラス

APIリファレンス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/a53bfad0-7195-2d7c-8b02-085fde9d3cc1.png)


https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.AwsIntegration.html

```typescript
const integration = new apigateway.AwsIntegration({
      service: 'states', //統合するサービスとしてStep Functionsを指定。ちなみにstatesはStep FunctionsのAPI名
      action: 'StartExecution', //実行するアクションの指定。指定されたステートマシンを実行する。
      integrationHttpMethod: 'POST', //API GatewayからStep Functionsにリクエストを送信する際のHTTPメソッド。Step FunctionsのStartExectionはPOSTを要求するのでPOSTでないといけない
      options: {
        credentialsRole: apiGatewayRole, //APIGatewayがStep Functionsにリクエストを送信する際に使用するIAMロール。事前に定義していたもの。
        integrationResponses: [{ //Step FunctionsからAPIGatewayへのレスポンス。
          statusCode: '200',
        }],
        requestTemplates: { //API Gatewayにリクエストが送られたときのリクエストボディ全体をinputとしてステップファンクションに渡す
          'application/json': `{
            "input": "$util.escapeJavaScript($input.body)", //APIGatewayに送信されたリクエストボディ全体を取得してそのまま渡す。
            "stateMachineArn": "${props.stateMachineArn}" //このコンストラクトのプロパティとして渡されるステートマシンのARN。
          }`
        },
      },
    });
```