AWS CDK + TypeScript で API Gateway + Lambda 構成を作る
前回は 公式 CDK Workshop を見ながら Lambda 関数をデプロイしました。このままでは外側から呼び出すことができないので、API Gateway と連携させてみることにします。
上記の公式 Workshop の続きに API Gateway との連携が用意されているので、その通りにやっていきます。
API Gateway を追加する
(CDK プロジェクトは前回から引き継いだものを使います)
まずは依存するパッケージのインストールから。
npm install @aws-cdk/aws-apigateway
次に lib/example-cdk-hello-lambda-stack.ts の変更を行います。まずは先頭に入れたパッケージの import を追記。
import * as apigw from "@aws-cdk/aws-apigateway";
そして Lambda 関数をデプロイしている処理の下に下記コードを挿入。
new apigw.LambdaRestApi(this, "Endpoint", {
handler: hello,
});
えっ、これだけ? すごい。
たった数行のこのコードだけで API Gateway で Lambda 統合プロキシをセットアップして Lambda 関数と紐づけまでできちゃうみたいです。
cdk diff
プロビジョニングされているリソースと今回定義したリソースの差分を見てみます。
cdk diff
出力結果
Stack ExampleCdkHelloLambdaStack
IAM Statement Changes
┌───┬────────────────────────┬────────┬────────────────────────┬────────────────────────┬────────────────────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼────────────────────────┼────────┼────────────────────────┼────────────────────────┼────────────────────────┤
│ + │ ${Endpoint/CloudWatchR │ Allow │ sts:AssumeRole │ Service:apigateway.ama │ │
│ │ ole.Arn} │ │ │ zonaws.com │ │
├───┼────────────────────────┼────────┼────────────────────────┼────────────────────────┼────────────────────────┤
│ + │ ${HelloHandler.Arn} │ Allow │ lambda:InvokeFunction │ Service:apigateway.ama │ "ArnLike": { │
│ │ │ │ │ zonaws.com │ "AWS:SourceArn": "ar │
│ │ │ │ │ │ n:${AWS::Partition}:ex │
│ │ │ │ │ │ ecute-api:${AWS::Regio │
│ │ │ │ │ │ n}:${AWS::AccountId}:$ │
│ │ │ │ │ │ {EndpointEEF1FD8F}/${E │
│ │ │ │ │ │ ndpoint/DeploymentStag │
│ │ │ │ │ │ e.prod}/*/{proxy+}" │
│ │ │ │ │ │ } │
│ + │ ${HelloHandler.Arn} │ Allow │ lambda:InvokeFunction │ Service:apigateway.ama │ "ArnLike": { │
│ │ │ │ │ zonaws.com │ "AWS:SourceArn": "ar │
│ │ │ │ │ │ n:${AWS::Partition}:ex │
│ │ │ │ │ │ ecute-api:${AWS::Regio │
│ │ │ │ │ │ n}:${AWS::AccountId}:$ │
│ │ │ │ │ │ {EndpointEEF1FD8F}/tes │
│ │ │ │ │ │ t-invoke-stage/*/{prox │
│ │ │ │ │ │ y+}" │
│ │ │ │ │ │ } │
│ + │ ${HelloHandler.Arn} │ Allow │ lambda:InvokeFunction │ Service:apigateway.ama │ "ArnLike": { │
│ │ │ │ │ zonaws.com │ "AWS:SourceArn": "ar │
│ │ │ │ │ │ n:${AWS::Partition}:ex │
│ │ │ │ │ │ ecute-api:${AWS::Regio │
│ │ │ │ │ │ n}:${AWS::AccountId}:$ │
│ │ │ │ │ │ {EndpointEEF1FD8F}/${E │
│ │ │ │ │ │ ndpoint/DeploymentStag │
│ │ │ │ │ │ e.prod}/*/" │
│ │ │ │ │ │ } │
│ + │ ${HelloHandler.Arn} │ Allow │ lambda:InvokeFunction │ Service:apigateway.ama │ "ArnLike": { │
│ │ │ │ │ zonaws.com │ "AWS:SourceArn": "ar │
│ │ │ │ │ │ n:${AWS::Partition}:ex │
│ │ │ │ │ │ ecute-api:${AWS::Regio │
│ │ │ │ │ │ n}:${AWS::AccountId}:$ │
│ │ │ │ │ │ {EndpointEEF1FD8F}/tes │
│ │ │ │ │ │ t-invoke-stage/*/" │
│ │ │ │ │ │ } │
└───┴────────────────────────┴────────┴────────────────────────┴────────────────────────┴────────────────────────┘
IAM Policy Changes
┌───┬────────────────────────────┬───────────────────────────────────────────────────────────────────────────────┐
│ │ Resource │ Managed Policy ARN │
├───┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ + │ ${Endpoint/CloudWatchRole} │ arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonAPIGatewayPushToClou │
│ │ │ dWatchLogs │
└───┴────────────────────────────┴───────────────────────────────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Resources
[+] AWS::ApiGateway::RestApi Endpoint EndpointEEF1FD8F
[+] AWS::IAM::Role Endpoint/CloudWatchRole EndpointCloudWatchRoleC3C64E0F
[+] AWS::ApiGateway::Account Endpoint/Account EndpointAccountB8304247
[+] AWS::ApiGateway::Deployment Endpoint/Deployment EndpointDeployment318525DAc5ea4e80a778e1689ca3a2bce5ed88de
[+] AWS::ApiGateway::Stage Endpoint/DeploymentStage.prod EndpointDeploymentStageprodB78BEEA0
[+] AWS::ApiGateway::Resource Endpoint/Default/{proxy+} Endpointproxy39E2174E
[+] AWS::Lambda::Permission Endpoint/Default/{proxy+}/ANY/ApiPermission.ExampleCdkHelloLambdaStackEndpoint7EE55095.ANY..{proxy+} EndpointproxyANYApiPermissionExampleCdkHelloLambdaStackEndpoint7EE55095ANYproxy24FC3890
[+] AWS::Lambda::Permission Endpoint/Default/{proxy+}/ANY/ApiPermission.Test.ExampleCdkHelloLambdaStackEndpoint7EE55095.ANY..{proxy+} EndpointproxyANYApiPermissionTestExampleCdkHelloLambdaStackEndpoint7EE55095ANYproxy33B39215
[+] AWS::ApiGateway::Method Endpoint/Default/{proxy+}/ANY EndpointproxyANYC09721C5
[+] AWS::Lambda::Permission Endpoint/Default/ANY/ApiPermission.ExampleCdkHelloLambdaStackEndpoint7EE55095.ANY.. EndpointANYApiPermissionExampleCdkHelloLambdaStackEndpoint7EE55095ANYCFA1F0D0
[+] AWS::Lambda::Permission Endpoint/Default/ANY/ApiPermission.Test.ExampleCdkHelloLambdaStackEndpoint7EE55095.ANY.. EndpointANYApiPermissionTestExampleCdkHelloLambdaStackEndpoint7EE55095ANYD177CB2C
[+] AWS::ApiGateway::Method Endpoint/Default/ANY EndpointANY485C938B
Outputs
[+] Output Endpoint/Endpoint Endpoint8024A810: {"Value":{"Fn::Join":["",["https://",{"Ref":"EndpointEEF1FD8F"},".execute-api.",{"Ref":"AWS::Region"},".",{"Ref":"AWS::URLSuffix"},"/",{"Ref":"EndpointDeploymentStageprodB78BEEA0"},"/"]]}}
あのたった数行を追加しただけで、IAM 設定から API Gateway 作成 と Lambda 関数への紐づけを行おうとしていることが分かります。
CloudFormation テンプレートを確認する
以下のコマンドで CloudFormation テンプレートが標準出力されます。
cdk synth
出力結果(一部抜粋)
EndpointproxyANYC09721C5:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: ANY
ResourceId:
Ref: Endpointproxy39E2174E
RestApiId:
Ref: EndpointEEF1FD8F
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri:
Fn::Join:
- ""
- - "arn:"
- Ref: AWS::Partition
- ":apigateway:"
- Ref: AWS::Region
- :lambda:path/2015-03-31/functions/
- Fn::GetAtt:
- HelloHandler2E4FBA4D
- Arn
- /invocations
すべてのリクエストを Lambda 関数へ流していることが見て取れます。
Lambda 統合プロキシの場合は IntegrationHttpMethod が常に POST になるそうです。
Lambda 統合では、関数呼び出しの Lambda サービスアクションの仕様に従って、統合リクエストに POST の HTTP メソッドを使用する必要があります。
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
API Gateway から Lambda 関数へは常に POST になるから? だそうで。
デプロイしてブラウザから確認する
cdk deploy
✅ ExampleCdkHelloLambdaStack
Outputs:
ExampleCdkHelloLambdaStack.Endpoint = https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/
最後に出力された URL にアクセスしてみましょう。
HTTP ステータスコード 200 で text/plain で返ってきました🤘
まとめ
AWS CDK を用いて Lambda 関数をデプロイして API Gateway との統合をすることができました。
独自ドメインは? 単体テストは? TypeScript 化は? とまだまだ深堀できる部分がありますので、やっていきたいと思います。
正直なところ、今の段階ではまだ Serverless Framework を選択するかなぁと思いました。やっぱりプロジェクト作成時に TypeScript のボイラーテンプレート選べるの強い。とはいえ CDK の方がインフラ部分も TypeScript で定義できるので、TypeScript の勉強もかねてしばらく付き合っていこうと思います。
😉