🐙

AWS CDKでサーバレスAPIをJavaで作ってみた

2021/06/26に公開

AWS CDKに慣れるためにJavaでCDKを書いてサーバレスAPI(Lambda + API Gateway)を作ってみました。

CDKのインストール&セットアップ

まずはCDKのインストールになります。

$ npm install -g aws-cdk

$ cdk --version
1.109.0 (build c647e38)

つづいてCDKのセットアップになります。

$ mkdir cdk_hello && cd cdk_hello
$ cdk init sample-app --language java
$ cdk bootstrap

これで各種ファイルが作成されました。あとは設定コードを書いていくだけです。

補足ですが、スイッチロールして作業を行う場合はAWS CLIと同様にcdk bootstrap --profile XXのようにして実行可能です。

Lambda&APIGateway作成

公式のチュートリアルではLambdaを作成してからAPI Gatewayの作成という順番になっていますが、どちらも一緒に作成してしまいます。

pom.xmlに追加

software.amazon.awscdk.lambdasoftware.amazon.awscdk.apigatewayのライブラリを追加します。

pom.xml
<dependencies>
    <!-- AWS Cloud Development Kit -->
    <dependency>
        <groupId>software.amazon.awscdk</groupId>
        <artifactId>core</artifactId>
        <version>${cdk.version}</version>
    </dependency>

    <!-- ここから追加 -->
    <!-- Respective AWS Construct Libraries -->
    <dependency>
        <groupId>software.amazon.awscdk</groupId>
        <artifactId>lambda</artifactId>
        <version>1.109.0</version>
    </dependency>

    <!-- Respective AWS Construct Libraries -->
    <dependency>
        <groupId>software.amazon.awscdk</groupId>
        <artifactId>apigateway</artifactId>
        <version>1.109.0</version>
    </dependency>
    <!-- ここまで -->

    中略
</dependencies>

Lambdaとして動くコードを作成

$ pwd
/cdk_hello

$ mkdir lambda
$ touch lambda/hello.js
hello.js
exports.handler = async function(event) {
  console.log("request:", JSON.stringify(event, undefined, 2));
  return {
    statusCode: 200,
    headers: { "Content-Type": "text/plain" },
    body: `Hello, CDK! You've hit ${event.path}\n`
  };
};

CDKのコード

それではLambdaとAPI Gatewayを作るためのコードを書いていきます。

src/main/java/com/myorg/CdkHelloStack.java
package com.myorg;

import software.amazon.awscdk.core.Construct;
import software.amazon.awscdk.core.Stack;
import software.amazon.awscdk.core.StackProps;
import software.amazon.awscdk.services.apigateway.LambdaRestApi;
import software.amazon.awscdk.services.lambda.Code;
import software.amazon.awscdk.services.lambda.Function;
import software.amazon.awscdk.services.lambda.Runtime;

public class CdkHelloStack extends Stack {
    public CdkHelloStack(final Construct parent, final String id) {
        this(parent, id, null);
    }

    public CdkHelloStack(final Construct parent, final String id, final StackProps props) {
        super(parent, id, props);

        final Function hello = Function.Builder.create(this, "HelloHandler")
                .runtime(Runtime.NODEJS_14_X)
                .code(Code.fromAsset("lambda"))
                .handler("hello.handler")
                .build();

        LambdaRestApi.Builder.create(this, "Endpoint")
                .handler(hello)
                .build();
    }
}

もともとCdkHelloStack(final Construct parent, final String id, final StackProps props)メソッドにはキューを作成するコードがありましたが、今回は利用しないので削除し、代わりにFunctionとLambdaRestApiをインスタンス化するコードを記載しました。
なんとこれだけでOKです。

あとはデプロイするだけになりますが、このままだとテストコードでエラーが出てしまうので修正しておきます。

src/test/java/com/myorg/CdkHelloStackTest.java
    @Test
    public void testStack() throws IOException {
        App app = new App();
        CdkHelloStack stack = new CdkHelloStack(app, "test");

        JsonNode actual = JSON.valueToTree(app.synth().getStackArtifact(stack.getArtifactId()).getTemplate());

        assertThat(actual.toString())
            .contains("AWS::Lambda::Function")
            .contains("AWS::ApiGateway::RestApi");
    }

diffの取得

$ mvn package
$ cdk diff

これで実際の環境と今回修正した内容の差分が見れます。
以下はdiff出力の抜粋になります。

Resources
[+] AWS::IAM::Role HelloHandler/ServiceRole HelloHandlerServiceRole 
[+] AWS::Lambda::Function HelloHandler HelloHandler
[+] AWS::ApiGateway::RestApi Endpoint Endpoint
[+] AWS::IAM::Role Endpoint/CloudWatchRole EndpointCloudWatchRole
[+] AWS::ApiGateway::Account Endpoint/Account EndpointAccount 
[+] AWS::ApiGateway::Deployment Endpoint/Deployment EndpointDeploymentXXXXXXX
[+] AWS::ApiGateway::Stage Endpoint/DeploymentStage.prod EndpointDeploymentStageprod
[+] AWS::ApiGateway::Resource Endpoint/Default/{proxy+} Endpointproxy
[+] AWS::Lambda::Permission Endpoint/Default/{proxy+}/ANY/ApiPermission.CdkHelloStackEndpoint115409AD.ANY..{proxy+} EndpointproxyANYApiPermissionCdkHelloStackEndpointANYproxy
[+] AWS::Lambda::Permission Endpoint/Default/{proxy+}/ANY/ApiPermission.Test.CdkHelloStackEndpoint.ANY..{proxy+} EndpointproxyANYApiPermissionTestCdkHelloStackEndpointANYproxy
[+] AWS::ApiGateway::Method Endpoint/Default/{proxy+}/ANY EndpointproxyANY
[+] AWS::Lambda::Permission Endpoint/Default/ANY/ApiPermission.CdkHelloStackEndpoint.ANY.. EndpointANYApiPermissionCdkHelloStackEndpointANY
[+] AWS::Lambda::Permission Endpoint/Default/ANY/ApiPermission.Test.CdkHelloStackEndpoint.ANY.. EndpointANYApiPermissionTestCdkHelloStackEndpoint
[+] AWS::ApiGateway::Method Endpoint/Default/ANY EndpointANY

deploy

あとはデプロイです。

$ cdk deploy
Do you wish to deploy these changes (y/n)? y

CdkHelloStack.Endpoint8024A810 = https://XXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/prod/

返却されたURLに対してアクセスしてみます。

$ curl https://XXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/prod/
Hello, CDK! You've hit /

後片付け

作成したリソースを削除する場合はcdk destroyを実行します。

$ cdk destroy
Are you sure you want to delete: CdkHelloStack (y/n)? y
 ✅  CdkHelloStack: destroyed

参考URL

AWS Cloud Development Kit(CDK) -> Getting started with the AWS CDK
AWS CDK Intro Workshop > Java Workshop > New Project
AWS CDK Intro Workshop > Java Workshop > Hello, CDK!

Discussion