Lambda(Go)でDynamoDBのデータ取得・登録(GET/POST)をしてみた

2023.04.10

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

こんにちは、ゲームソリューショングループのsoraです。
今回は、Lambda(Go)でDynamoDBのデータ取得・登録(GET/POST)をしてみたことを書いていきます。

データは都道府県名・県庁所在地・地域をカラムとして持つもので、都道府県名を渡すことで県庁所在地と地域を取得でき、全ての値を与えることで登録もできるようにしていきます。

LambdaとDynamoDBの作成

今回はTerraformを使って作成します。

Terraformソースコード

まずは、AWS側で必要なサービスを作成します。

main.tf

terraform {
    #AWSプロバイダーのバージョン指定
    required_providers {
        aws = {
            source  = "hashicorp/aws"
            version = "~> 4.51.0"
        }
    }
}
#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               = "Prefecture_Lambda_Role"
    assume_role_policy = data.aws_iam_policy_document.assume_role.json
    managed_policy_arns=["arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess"]
}

#Lambdaへの配置ファイルのzip化
data archive_file lambda {
    type        = "zip"
    source_file = "dynamodbapi"
    output_path = "handler.zip"
}
#Lambdaの作成
resource aws_lambda_function Prefecture_Lambda {
    filename      = "handler.zip"
    function_name = "DynamoDBAPI_Lambda"
    role          = aws_iam_role.iam_for_lambda.arn
    handler       = "dynamodbapi"
    source_code_hash = data.archive_file.lambda.output_base64sha256
    runtime = "go1.x"
}

#DynamoDBの作成
resource aws_dynamodb_table prefecture_table {
    name           = "PrefecturesTable"
    billing_mode   = "PAY_PER_REQUEST"
    hash_key       = "PrefectureName"
    range_key      = "Region"
    attribute {
        name = "PrefectureName"
        type = "S"
    }
    attribute {
        name = "Region"
        type = "S"
    }
}
#初期データファイルの取得
locals {
    csv_data = file("prefectual.csv")
    dataset = csvdecode(local.csv_data)
}
#テーブルアイテムの登録
resource aws_dynamodb_table_item table_item {
    for_each = { for record in local.dataset : record.prefecturename => record }
    table_name = aws_dynamodb_table.prefecture_table.name
    hash_key   = aws_dynamodb_table.prefecture_table.hash_key
    range_key  = aws_dynamodb_table.prefecture_table.range_key
    item = <<ITEM
    {
        "PrefectureName": {"S": "${each.value.prefecturename}"},
        "PrefecturalCapital": {"S": "${each.value.prefecturalcapital}"},
        "Region": {"S": "${each.value.region}"}
    }
    ITEM
}

DynamoDBへ初期値として登録するデータは、CSVファイルに記載してTerraform側でfor_eachを使って登録します。

prefectual.csv

prefecturename,prefecturalcapital,region
Hokkaido,Sapporo,Hokkaido
Tokyo,Tokyo,Kanto
Aichi,Nagoya,Chubu
Osaka,Osaka,Kansai
Hukuoka,Hakata,Kyushu

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

Goソースコード

GETメソッドで都道府県名を渡すことで県庁所在地と地域を取得でき、POSTメソッドでデータの登録ができる関数です。

aws-sdk-goを直接使う場合は複雑になるため、今回はguregu/dynamoを使用しています。
エラー処理は全然入れていないため、実際に使う場合は入れた方が良いです。

今回はAPI Gatewayまでは作りませんが、後に接続することを意識してAPIGatewayProxyRequestの形で受け取るようにしています。

dynamodb_connection.go

package main

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

type Event struct {
    PrefectureName string `json:"PrefectureName"`
    Region string `json:"Region"`
    PrefecturalCapital string `json:"PrefecturalCapital"`
}

// メソッドに応じて処理分岐
func DBOperateAPI(req events.APIGatewayProxyRequest) () {
    db := dynamo.New(session.New(), &aws.Config{Region: aws.String("ap-northeast-1")})
    table := db.Table("PrefecturesTable")
    switch req.HTTPMethod {
    case "GET":
        prefecture_name := req.QueryStringParameters["PrefectureName"]
        var results []Event
        // ソートキーまで指定する場合は
        // table.Get("PrefectureName", prefecture_name).Range("Region", dynamo.Equal, prefecture_region).One(&result)
        // PrefectureNameが一致するものを全て取得
        table.Get("PrefectureName", prefecture_name).All(&results)
        fmt.Printf("%v\n", results)
    case "POST":
        prefecture_name := req.QueryStringParameters["PrefectureName"]
        prefecture_region := req.QueryStringParameters["Region"]
        prefecture_capital := req.QueryStringParameters["PrefecturalCapital"]
        evt := Event{PrefectureName: prefecture_name, Region: prefecture_region, PrefecturalCapital: prefecture_capital}
        table.Put(evt).Run()
    }
}

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

デプロイ

コードの準備ができたため、実行していきます。

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

GoファイルをLambdaに持っていく前に、ビルドをする必要があります。

$ go mod tidy
$ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o dynamodbapi

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

Terraform実行

作成したTerraformのコードを実行して構築します。

$ terraform init
$ terraform apply

正常に構築できていることが確認できます。
DynamoDBに初期値として登録したデータも登録されています。

Lambdaでのテスト

構築できたのでLambdaでテストしていきます。
テストイベントのJSONは、APIGatewayProxyRequestの形式に合わせて設定します。

まず検索から実行します。

{
    "HTTPMethod": "GET",
    "QueryStringParameters": {
        "PrefectureName": "Hokkaido"
    }
}

printfで表示させているため、ログ出力に取得した結果が表示されています。

次に登録を実行します。

{
    "HTTPMethod": "POST",
    "QueryStringParameters": {
        "PrefectureName": "Gifu",
        "Region": "Chubu",
        "PrefecturalCapital": "Gifu"
    }
}

DynamoDBを見ると、POSTで渡したデータが登録されています。

参考記事

気楽にDynamoDBを使おう
API Gateway から Lambda にパラメータを渡す

最後に

今回は、Lambda(Go)でDynamoDBへのデータ取得・登録(GET/POST)してみたことを記事にしました。
どなたかの参考になると幸いです。