AWS CDKでAPIGatewayのエンドポイントに異なるスタックに存在するLambdaを設定する方法

APIGatewayとLambdaを紐づける方法です。

前提知識

  • TypeScript
  • AWS CDKおよびCloudFormationの基本知識

やりたいこと

  • APIGateway(REST API)のエンドポイントにLambdaを設定する

やらないこと

  • VPC、IAMの考慮
  • AWS CDKの設定などを含めた詳細な説明

APIGatewayをデプロイするためのスタックファイル

APIGatewayのスタックではとりあえずエンドポイントにMockを設定します。
仮に1つのスタックファイルでAPIGatewayのエンドポイントにLambda設定する場合には、
MockIntegrationLambdaIntegrationに変えるだけです。

参考:AWS CDKでAPI Gateway+Lambdaを作成する際のベストなスタック構成について | DevelopersIO

なお、以下のスタックファイルで出来るURIhttps://ドメイン/root/fooとなります。

import * as cdk from "@aws-cdk/core";
import * as apigateway from "@aws-cdk/aws-apigateway";

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

    // api gateway
    const api = new apigateway.RestApi(this, "RestApi", {
      restApiName: "sample-api",
      description: "example for cdk stack"
    });

    // mock
    const dummyUser = {
      name: "dummy",
      description: "dummy user"
    };
    const mock = new apigateway.MockIntegration({
      requestTemplates: { "application/json": `{"statusCode": 200}` },
      integrationResponses: [
        {
          statusCode: "200",
          responseTemplates: {
            "application/json": JSON.stringify(dummyUser)
          }
        }
      ]
    });

    // add resource
    const root = api.root.addResource("root");
    root
      .addResource("foo")
      .addMethod("GET", mock, { methodResponses: [{ statusCode: "200" }] });
  }
}

Lambdaをデプロイするスタックファイル

デプロイしたAPIGatewayのエンドポイントにLambdaを設定します。
上記のスタックでは1つのファイル内でLambda(モック)とAPIGatewayをデプロイしましたが、
こちらのファイルではLambda(モック)のみデプロイします。
また、APIGatewayと紐づける際にはURIhttps://ドメイン/root/barに設定します。

import * as cdk from "@aws-cdk/core";
import * as apigateway from "@aws-cdk/aws-apigateway";

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

    // lambda
    const mockLambda = new apigateway.MockIntegration();

    // from rest api
    const apiAttr = { restApiId: "APIGatewayのID", rootResourceId: "ルートパスのID" };
    const restApi = apigateway.RestApi.fromRestApiAttributes(
      this,
      "from rest",
      apiAttr
    );
    const resource = apigateway.Resource.fromResourceAttributes(
      this,
      "resource",
      {
        restApi,
        resourceId: "取得したいパスのリソースID",
        path: "root"
      }
    );
    resource.addResource("bar").addMethod("GET", mockLambda);
  }
}

まとめ

異なるスタックに存在するAPIGatewayとLambdaを紐づける際には、
IDでAPIGatewayを取得してから、APIGatewayに存在するリソースを取得する必要があるようです。
公式のドキュメントを読むと、IResourceインターフェースにgetResourceresourceForPathなどの既存リソースを取得するメソッドが用意されているのですが、こちらではうまくいなかったため上記のような方法を取りました。
リソースのIDなどを指定する必要があり、スマートではないですがやりたい事はできたので記録として残します。