Ponz Dev Log

ゆるくてマイペースな開発日記

Serverless FrameworkでKotlin & LambdaのサーバーレスAPIを作る

朝活.Kotlinで作ったServerless Framework × Kotlin × LambdaのサーバーレスAPIまとめです。

justincase.connpass.com

serverless.com

概要

  • 題材

    • 松屋のメニューの写真をひたすら貼るSlack Bot. (ちょっとしたジョークアプリです)
  • 上記のSlackBotのバックエンドをLambda関数で実装する.

    • API管理: API Gateway
    • ロジック: AWS Lambda <--ここをKotlinで書いた
  • CI/CDファーストを目標に、なるべくも手作業を減らしてビルド&デプロイを簡単にする.

やったこと

まずアプリケーションを作成するときに悩むのが、ボイラープレートコード(フレームワーク等で必要になる典型的なコード)の扱い。 ビジネスロジックを書くことに集中できるのがサーバーレスアプリケーションの強みの1つなので、LambdaとAPI Gatewayの接続部分や、エントリーポイントのHandlerクラスはなるべく書きたくないところ。

実はServerless Frameworkのテンプレートから雛形は自動生成可能です(プラットフォームによってはない)。 sls create --helpを打って調べる限り, 使えるテンプレートは3つありますね。KotlinでJS使うテンプレートなんてあるのか...

それでは雛形をコマンドラインから作成。事前にnpm i -g serverlessでserverlessコマンドは入れておきましょう。今回は"aws-kotlin-jvm-maven"を使います。

serverless create \
    --template aws-kotlin-jvm-maven \
    --path matsuya-finder \
    --name matsuya-finder

5秒くらいでテンプレートは作成されます。指定した--path以下は以下のようなディレクトリ構成になっています。

.
 |- pom.xml
 |- serverless.yml
 |- src
    |- main
       |- kotlin
          |- com
             |- serverless
                |- ApiGatewayResponse.kt
                |- Handler.kt
                |- Response.kt
  • ApiGatewayResponse.kt ... Lambda --> API Gatewayにレスポンスを返す時のデータの格納クラス。弄らずにそのまま使う
  • Handler.kt ... アプリケーションのエントリーポイント。ビジネスロジックはここにInjectする。
  • Response.kt ... アプリケーションのレスポンス。data classになっている。
  • serverless.yml ... Serverless Frameworkに管理されたアプリケーションのデプロイ定義。裏ではCloudFormationを使っている。

あとは業務ロジックのコードを好きに書きます!

最終的なコードは以下の図のような構成になりました。 アプリのコードが重くなければ、KtorやJavaliなどのWeb Application Frameworkを使うよりもスッキリしてますね!

f:id:accelerk:20190323235930p:plain
ディレクトリ構造 最終形

また、serverless.ymlは以下の構成になっています。このファイルが置かれている場所で sls deploy を発行すればデプロイ完了です。

service: chom-matsuya-finder
  
# Base Definition
provider:
  name: aws
  runtime: java8
  stage: dev
  region: ap-northeast-1

# Packaging information
package:
  artifact: target/matsuya-finder-dev.jar

# Individual Function Definitions
functions:
  main:
    handler: net.ponzmild.Handler
    name: matsuya-finder
    description: Fetch today's Matsuya menu.
    events:
      - http:
          method: post
          path: matsuya

Tips

  • Serverless Frameworkのコマンドを打つときは, 権限のあるプロファイルを使おう

    • IAMロールをつけたのになぜかUnauthorizedとなってしまうことありますが、だいたいはIAMロールをつけたユーザーでCLIを叩いてないことが原因です。
    • (使用例) sls deploy --aws-profile your-serverless-agent
  • そのままデプロイすると統合リクエストのタイプが"Lambda Proxy"になる

    • このタイプはリクエストのコンテントタイプを細かく指定しづらいです。
    • また、雛形として自動生成されるApiGatewayResponse.ktはLambda Proxyタイプ用のレスポンス定義です!これ以外のタイプは別途レスポンスのクラスを用意しましょう。
    • serverless.ymlintegration:lambdaを指定するか、Handlerクラス内でパースのどちらかで解決しなければなりません。
    • 今回は後者にしています(以下のコード)
class Handler : RequestHandler<Map<String, Any>, ApiGatewayResponse> {
    override fun handleRequest(input: Map<String, Any>, context: Context): ApiGatewayResponse {
        // API GatewayのイベントのJSONからリクエストボディのみ取り出す
        val inputJsonString = input.get("body") as String

        // 全て文字列なので, Mapに詰め替える
        var inputBodyMap: HashMap<String, String> = hashMapOf()
        val kvStrings = inputJsonString.split("&")
        kvStrings.forEach {
            val kv = it.split("=")
            inputBodyMap.put(kv[0], kv[1]) // 詰め替え
        }

        // And more...
    }
}

Kotlinも結構簡単にサーバーレスなAPIを作れるんですね。他にもS3のイベントやCloudWatch Eventsにも対応できるのでできる幅も広そう。 また、Androidユーザーはフロントエンドと同じ言語を使えるからいいですね。iOSユーザーのためにSwiftでもできるのかな?

以上。