Lambda(Go)で猫のエネルギー供給量を計算するAPIを作ってAPI Gatewayでテストしてみた

2023.04.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、ゲームソリューショングループのsoraです。
今回は、Lambda(Go)で猫の1日当たりのエネルギー供給量を計算するAPIを作って、API Gatewayでテストしてみたことについて書いていきます。

ちなみに、API Gatewayを使わずともLambda上でテストも可能です。
今回はあえて無駄にAPI Gatewayを使用します。

Lambdaで使用するコード(Go)

Goソースコード

猫の体重と生後何か月かを与えると、猫の1日の食事量(kcal)の計算をしてくれる関数です。
計算式についてはあくまで参考程度でお願いいたします。

LambdaでGoを使用するときは、6行目のGo用のLambdaプログラミングモデルをimportする必要があります。

api.go

package main

import (
    "fmt"
	// Go用のLambdaプログラミングモデル
    "github.com/aws/aws-lambda-go/lambda"
)

type GetData struct {
    Weight float32 `json:"Weight"`
    Month int `json:"Month"`
}

type ReturnData struct {
    Answer string `json:"Answer"`
}

func CatCalAPI(event GetData) (ReturnData, error) {
	var per float32 = 30 * event.Weight + 70
	switch {
	case event.Month <= 4:
		var kcal float32 = per * 3.0
		return ReturnData{Answer: fmt.Sprintf("%.1f kcal", kcal)},nil
	case event.Month <= 6:
		kcal := per * 2.5
		return ReturnData{Answer: fmt.Sprintf("%.1f kcal", kcal)},nil
	case event.Month <= 12:
		kcal := per * 2.0
		return ReturnData{Answer: fmt.Sprintf("%.1f kcal", kcal)},nil
	case event.Month <= 95:
		kcal := per * 1.2
		return ReturnData{Answer: fmt.Sprintf("%.1f kcal", kcal)},nil
	case event.Month > 95 :
		kcal := per * 1.1
		return ReturnData{Answer: fmt.Sprintf("%.1f kcal", kcal)},nil
	default :
		return ReturnData{Answer: fmt.Sprintf("error")},nil
	}
}

func main() {
    lambda.Start(CatCalAPI)
}

作成したGoファイルのビルド

GoファイルをLambdaに持っていく前に、ビルドをする必要があります。
Terraformでlocal-execを使ってビルドコマンドもまとめることもできますが、今回は手動で実施します。

Terraformにまとめたい方は以下ページを参考にしてください。
GolangのLambda関数をTerraformだけでデプロイする

GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o api

ビルドが終わったら、apiという名前のファイルができています。

LambdaとAPI Gatewayの作成

Terraformソースコード

AWS側で必要なサービスを作成していきます。
私は最近CDKと仲違いしたため、Terraformを使って作成します。

main.tf

terraform {
    #AWSプロバイダーのバージョン指定
    required_providers {
        aws = {
            source  = "hashicorp/aws"
            version = "~> 4.51.0"
        }
    }
    #tfstateファイルをS3に配置する(配置先のS3は事前に作成済み)
    backend s3 {
        bucket = "sora-tfstate-bucket"
        region = "ap-northeast-1"
        key    = "tf-test.tfstate"
    }
}

#AWSプロバイダーの定義
provider aws {
    region = "ap-northeast-1"
}

#Lambda用IAMロールの信頼関係の定義
data aws_iam_policy_document assume_role {
    statement {
        effect = "Allow"
        principals {
            type = "Service"
            identifiers = ["lambda.amazonaws.com"]
        }
        actions = ["sts:AssumeRole"]
    }
}

#Lambda用IAMロールの作成
resource aws_iam_role iam_for_lambda {
    name               = "CatAPI_Lambda_Role"
    assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

#Lambdaへの配置ファイルのzip化
data archive_file lambda {
    type        = "zip"
    source_file = "../api/api"
    output_path = "handler.zip"
}

#Lambdaの作成
resource aws_lambda_function CatAPI_Lambda {
    filename      = "handler.zip"
    function_name = "CatAPI_Lambda"
    role          = aws_iam_role.iam_for_lambda.arn
    handler       = "api"
    source_code_hash = data.archive_file.lambda.output_base64sha256
    runtime = "go1.x"
}


#API Gatewayの作成
##APIの作成
resource aws_api_gateway_rest_api CatAPIGateway {
    name = "CatAPIGateway"
    endpoint_configuration {
        types = ["REGIONAL"]
    }
}

##リソースの作成
resource aws_api_gateway_resource CatAPIResource {
    rest_api_id = aws_api_gateway_rest_api.CatAPIGateway.id
    parent_id   = aws_api_gateway_rest_api.CatAPIGateway.root_resource_id
    path_part   = "resource"
}

##メソッドの作成
resource aws_api_gateway_method CatAPIMethod {
    rest_api_id   = aws_api_gateway_rest_api.CatAPIGateway.id
    resource_id   = aws_api_gateway_resource.CatAPIResource.id
    http_method   = "GET"
    authorization = "NONE"
    request_parameters = {
        "method.request.querystring.Weight" = true
        "method.request.querystring.Month" = true
    }
}

##リクエスト統合の作成
resource aws_api_gateway_integration CatAPIIntegration {
    rest_api_id          = aws_api_gateway_rest_api.CatAPIGateway.id
    resource_id          = aws_api_gateway_resource.CatAPIResource.id
    http_method          = aws_api_gateway_method.CatAPIMethod.http_method
    integration_http_method = "POST"
    type = "AWS"
    uri = aws_lambda_function.CatAPI_Lambda.invoke_arn
    request_templates =  {
        "application/json" = <<-EOT
        {
            "Weight": $input.params('Weight'),
            "Month": $input.params('Month')
        }
        EOT
    }
}

##レスポンスメソッドの作成
resource aws_api_gateway_method_response CatAPIResponse {
    rest_api_id = aws_api_gateway_rest_api.CatAPIGateway.id
    resource_id = aws_api_gateway_resource.CatAPIResource.id
    http_method = aws_api_gateway_method.CatAPIMethod.http_method
    status_code = "200"
    response_models = {
        "application/json" = "Empty"
    }
}

##レスポンス統合の作成
resource aws_api_gateway_integration_response CatAPIResponseIntegration {
    rest_api_id = aws_api_gateway_rest_api.CatAPIGateway.id
    resource_id = aws_api_gateway_resource.CatAPIResource.id
    http_method = aws_api_gateway_method.CatAPIMethod.http_method
    status_code = aws_api_gateway_method_response.CatAPIResponse.status_code
}

41行目のarchive_fileで先ほどビルドして生成したファイルをzip化しています。
猫の体重(kg)を指すWeightと猫の生後何か月かを指すMonthの2つの値を与える必要があるため、API Gatewayのメソッドリクエストで必須パラメータとしています。

デプロイ

作成したTerraformのコードを実行して構築します。
CDKであればdeployですが、Terraformはapplyなのは間違えがちです。

terraform init
terraform apply

LambdaとAPI Gatewayを確認すると、正常に構築できていることが確認できます。

API Gatewayでのテスト

構築できたのでAPI Gatewayでテストしていきます。
私の飼っている猫は生後3か月で体重1kgのため、Month=3&Weight=1とします。 レスポンス本文のところに、猫の1日の食事量(kcal)が表示されています。

最後に

今回は、Lambda(Go)で猫の1日当たりのエネルギー供給量を計算するAPIを作って、API Gatewayでテストしてみたことを記事にしました。
どなたかの参考になると幸いです。