AWSLambda(Golang)×DynamoDBのローカル開発環境を整える

# 目的
AWS LambdaでバックエンドAPIを構築しているとき、関数の挙動を確認したいときは都度devにデプロイしていました。流石にローカルでの開発環境を整えたいと思ったのですがGoでの使い方の情報が少なかったので個人メモとしてまとめてみました。
今回はAPI GatewayがトリガーのLambda関数になります。
APIトリガー以外は
→[`serverless invoke local -f <function>` で十分らしいです](https://blog.70-10.net/2018/08/29/serverless-typescript/#4-code-classlanguage-textserverless-offlinecode%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%81%A7%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B)

# 全体の流れ

以下のJavascript filesのところを今回はGolangにしてやってみます。
<img width="1400" alt="https___qiita-image-store.s3.amazonaws.com_0_27509_b36f68ce-5e37-b597-2eae-0e67bd06d8dd.png (178.1 kB)" src="https://img.esa.io/uploads/production/attachments/1486/2021/06/02/92504/5dbead6f-6167-48eb-9d93-4c6cfa44d67e.png">

引用:https://qiita.com/noralife/items/e36621ddd0e5b8ff4447#%E6%A7%8B%E6%88%90

大まかな流れは以下です。
1. DynamoDB Localをインストール&セットアップ
2. Serverless offlineをインストール &セットアップ

# DynamoDB Localをインストール&セットアップ

## パッケージインストール

```sh
$ npm install --save-dev serverless-dynamodb-local
```

## serverless.ymlにプラグインを追記

```serverless.yml
plugins: 
 - serverless-dynamodb-local
```

## JDKがなければここでインストール
dynamodb-localのためにはJavaが必要でしたので、ローカルpcにJavaを入れていない方はインストールします。

インストールの仕方は、mac osだと、以下が参考になります。
https://qiita.com/suke_masa/items/f9af0fb84ad9447ae961


## dynamo db localをインストール

serverless.ymlのあるディレクトリにて、

```sh
$ sls dynamodb install
```

## カスタム定義

```serverless.yml
custom:
    dynamodb:
        stages:
          - dev
        start:
          port: 8000
          inMemory: true
          migrate: true
          seed: true
        seed:
          development:
            sources:
              - table: テーブル名
```

この状態で、

```sh
sls dynamodb start
```
でdynamodbがローカルで立ち上がります。

`http://localhost:8000/shell`

にアクセスすると管理画面に移れる。

シェル例

```
var params = {
    TableName: 'pr_enqueteform_dev',
};
dynamodb.scan(params, function(err, data) {
    if (err) ppJson(err);
    else ppJson(data);
});
```

# Serverless offlineをインストール &セットアップ

次にローカルでapi gatewayを試すためにServerless offlineをインストールしていきます。

## パッケージインストール

```sh
$ npm install --save-dev serverless-offline
```

## serverless.ymlにプラグインを追記

```serverless.yml
plugins: 
 - serverless-dynamodb-local
 - serverless-offline ### 追記
```

## カスタム定義


```serverless.yml
custom:
  serverless-offline:
    useDocker: true
```

この状態で

```sh
npx sls offline
```

でローカルで起動できます。

※Makefileにて、

```sh
make local-test
```

を打つことでビルド〜offline起動までやってくれるようにしています。

例:

```sh
$ npx sls offline
Serverless: Deprecation warning: CLI options definitions were upgraded with <中略>
   ┌───────────────────────────────────────────────────────────────────────────────────────┐
   │                                                                                       │
   │   POST | http://localhost:3000/dev/submit                                             │
   │   POST | http://localhost:3000/2015-03-31/functions/EnqueteformReceiver/invocations   │
   │                                                                                       │
   └───────────────────────────────────────────────────────────────────────────────────────┘

offline: [HTTP] server ready: http://localhost:3000 🚀
offline:
offline: Enter "rp" to replay the last request

offline: POST /dev/submit (λ: EnqueteformReceiver)
```

上記みたいな感じになっていたら立ち上がっています🎉

# offlineとdynamodb-localを疎通させる

localでofflineを使ってlambda apiを叩いてログを出力してみると、
handlerのeventsの中の、

`events.APIGatewayProxyRequest.RequestContext.APIID`

が `"offlineContext_apiId"`　となっていました。

そこで、この部分で判断して、localのdynamodbクライアントを生成するか本番用のdynamodbクライアントを生成するかを条件分岐させることにします。

localのdynamodbクライアントの生成方法は以下でやります。

```go
func (c *DynamoDBClient) OfflineDynamoDBClient(region string, tableName string) *DynamoDBClient {
	sess := session.Must(session.NewSession())
	config := aws.Config{
		Region: aws.String(region),
		Endpoint: aws.String("http://host.docker.internal:8000"),
		DisableSSL: aws.Bool(true),
		Credentials: credentials.NewStaticCredentials("dummy", "dummy", "dummy"),
	}

	dynamodbClient := DynamoDBClient{
		tableName: tableName,
		dynamo:    dynamodb.New(sess, &config),
	}

	return &dynamodbClient
}
```

※もっとよい判定方法がありそうな感じです。


# 試してみる
ローカルでコードを編集した後、一度ビルドしなければローカルの方にも反映されませんので、

```sh
make clean
make build
npx sls offline
```

とします。

## curlで疎通してみる

sls offlineしたターミナルとは別のタブにて、

```sh
curl http://localhost:3000/dev/submit -X POST -H "Content-Type: application/json" -d '{"category":"hogecategory","email":"sample@gmail.com","address":"東京都", "hoge":{"id":"1001","text":"hogehoge"}}'
```

sls offlineしたターミナルのタブにてログが出てくる。

```sh
START RequestId: d09*7296d-***-1c72-545b-5**c58d99034 Version: $LATEST

<中略>

END RequestId: d09729?6d-b4e2-1c72-5*45b-5e**ec58d99034
REPORT RequestId: d09?7296d-*b4e2-1c72-545b-5c5*d99034	Init Duration: 189.68 ms	Duration: 5513.25 ms	Billed Duration: 5514 ms	Memory Size: 3008 MB	Max Memory Used: 34 MB
```
実際にローカルのdynamoDBにデータを挿入する関数であれば、
`http://localhost:8000/shell`
をみてみると挿入されていることが確認できます！


# 参考

[Serverless Framework での AWS Lambda + Go ローカル開発事情](https://qiita.com/frozenbonito/items/effcc31ca2117789f0e9)
[Serverless アプリケーションをローカルで開発する](https://qiita.com/noralife/items/e36621ddd0e5b8ff4447)
[serverless framework + TypeScript +DynamoDB のローカル環境](https://zenn.dev/maruware/articles/cac0052812c2e3293dd5)
[Go で DynamoDB Local を使った時にいろいろハマったのでメモ](https://bearmini.hatenablog.com/entry/2015/10/09/233849)
[example](https://gist.github.com/Tamal/02776c3e2db7eec73c001225ff52e827)