Serverless Framework × TypeScript のローカル開発環境構築メモ（DynamoDB Local 編）

[Serverless Framework × TypeScript のローカル開発環境構築メモ（Serverless Offline編）](https://qiita.com/0taUtan/items/aca988de4b0a53244950)
Serverless Framework × TypeScript のローカル開発環境構築メモ（DynamoDB Local 編）[今回]
[Serverless Framework × TypeScript のローカル開発環境構築メモ（S3 Local編）](https://qiita.com/0taUtan/items/4c4ab7b466e5c8c3c0ba)

[前回](https://qiita.com/0taUtan/items/aca988de4b0a53244950)はServerless Frameworkをローカル実行する方法を記事にしました。今回はその続きで、DynamoDBをローカルで立てるプラグイン[Serverless DynamoDB Local](https://www.serverless.com/plugins/serverless-dynamodb-local)について述べます。

# Serverless DynamoDB Local のインストール
前回と同様に`aws-nodejs-template`を使って環境を作成します。`template.yaml`ファイルを使って書く記事が沢山上がっているのですが、TypeScriptで書いた方が変数の取り込みなど便利すぎるのでtsで書く方法を述べます。（といってもyamlをJSONに直すだけですが笑）あとでリソース用のバケットも準備するのでディレクトリ名は`resources`としています。

````
$ serverless create --template aws-nodejs-typescript --path resources
$ cd resources
$ npm install
````
こんな感じのディレクトリ構成で進めます。`api`側は[前回](https://qiita.com/0taUtan/items/aca988de4b0a53244950)作ったAPIで、今回は新たに`resources`を足しました。

````
backends
├── api
│   ├── README.md
│   ├── node_modules
│   ├── package-lock.json
│   ├── package.json
│   ├── serverless.ts
│   ├── src
│   ├── tsconfig.json
│   └── tsconfig.paths.json
└── resources
│   ├── README.md
│   ├── node_modules
│   ├── package-lock.json
│   ├── package.json
│   ├── serverless.ts
│   ├── src
│   ├── tsconfig.json
│   └── tsconfig.paths.json
````
ここからは、[公式](https://www.serverless.com/plugins/serverless-dynamodb-local)に従ってインストールしていきましょう。まず、npmでserverless-dynamodb-localをインストールします。

````
$ npm install -D serverless-dynamodb-local
````
次に、`serverless.ts`の不要な部分を削除しつつ、次のように書き換えます。なお、`stage`は`"local"`としました。

````serverless.ts
import type { AWS } from "@serverless/typescript";

const serverlessConfiguration: AWS = {
  service: "resources",
  frameworkVersion: "2",
  plugins: ["serverless-esbuild", "serverless-dynamodb-local"],
  provider: {
    name: "aws",
    runtime: "nodejs14.x",
    stage: "local",
  },
  package: { individually: true },
  custom: {
    esbuild: {
      bundle: true,
      minify: false,
      sourcemap: true,
      exclude: ["aws-sdk"],
      target: "node14",
      define: { "require.resolve": undefined },
      platform: "node",
      concurrency: 10,
    },
  },
};

module.exports = serverlessConfiguration;
````
重要なのは、`plugins`の最後に`"serverless-dynamodb-local"`を足したことです。（**必ずビルド用プラグインの後に書いてください**）さらに、この環境に次のコマンドでDynamoDB Localをインストールします。

````
$ npx sls dynamodb install
````
以上でインストールは完了です！

# DynamoDB Localの起動
前項でServerless DynamoDB Localのインストールは完了しました。次は、`serverless.ts`の`resources`を足していきます。`serverless.ts`の`serverlessConfiguration`に次のキーとオブジェクトを足してください。

````serverless.ts
const serverlessConfiguration: AWS = {
  ...
  resources: {
    Resources: {
      DynamoDBTable: {
        Type: "AWS::DynamoDB::Table",
        Properties: {
          TableName: "test",
          AttributeDefinitions: [
            {
              AttributeName: "DataType",
              AttributeType: "S",
            },
            {
              AttributeName: "ID",
              AttributeType: "S",
            },
          ],
          KeySchema: [
            {
              AttributeName: "DataType",
              KeyType: "HASH",
            },
            {
              AttributeName: "ID",
              KeyType: "RANGE",
            },
          ],
          ProvisionedThroughput: {
            ReadCapacityUnits: 1,
            WriteCapacityUnits: 1,
          },
        },
      },
    },
  },
  ...
}
````
この`resorces`は[AWSの公式](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html)に従って書いていけば良いです。たくさんのオプションがあるので戸惑うかもしれませんが、基本はPK, SK, GSIなどのテーブル設定と1秒当たりのスループットの最大値を設定するだけです。

次に、DynamoDB LocalのConfigを書いていきます。`serverlessConfiguration.custom`に次を足します。

````serverless.ts
const serverlessConfiguration: AWS = {
  ...
  custom: {
    ...
    dynamodb: {
      stages: ["local"],
      start: {
        port: 8000,
        inMemory:true,
        migrate:true,
        seed: true,
      },
      seed: {
        local: {
          sources: [
            {
              table: "test",
              sources: ["./seed/test.json"],
            },
          ],
        },
      },
    },
  },
}
````
ここで、`stages`にはローカル実行したいステージを記載します。また、`start`にはローカル実行する場合の設定を書き、`start.seed=true`の場合はDB起動時のシード値として`seed`に書いたソースがインポートされます。なお、`seed/test.json`には以下のようなデータを入れました。

````seed/test.json
[
  {
    "DataType": "Hoge",
    "ID": "HOGE_1"
  },
  {
    "DataType": "HUGA",
    "ID": "HUGA_2"
  }
]
````

　ここまでできると、次のコマンドで起動できます。(**注意：Javaがインストールされていないとエラーが出ることがあるようです**)

````
$ npx sls dynamodb start
````

次が表示されると、`localhost:8000`にDynamoDBが立っていることになります。

````
Dynamodb Local Started, Visit: http://localhost:8000/shell
Serverless: DynamoDB - created table test
Seed running complete for table: test
````

#接続確認
ここではAPIも同時に立てて、DynamoDB Localにアクセスできるか確認しましょう。[前回](https://qiita.com/0taUtan/items/aca988de4b0a53244950)のようにLambda関数をローカルで実行します。

まず、sdkをインストールします。

````
$ npm install @aws-sdk/client-dynamodb
````

次に、Lambda関数を書いていきます。`api/src/functions/hello/handler.ts`に次を足します。

````api/src/functions/hello/handler.ts
import {
  DynamoDBClient,
  DynamoDBClientConfig,
  ScanCommand,
} from "@aws-sdk/client-dynamodb";
const ClientConfig: DynamoDBClientConfig = {
  endpoint: "http://localhost:8000",
};
const dynamodb = new DynamoDBClient(ClientConfig);
````
さらにハンドラ関数内で次のようにDynamoDBアクセスを行います。ここではそのままレスポンスにDBのデータを入れました。

````api/src/functions/hello/handler.ts
const hello: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (
  event
) => {
  ...
  const result = await dynamodb.scan({
    TableName:"test"
  }).promise();
  const data = result.Items;
  ...
  return formatJSONResponse({
    data,
    event,
  });
};
````

ここまでやれば、あとはローカルで実行してみるだけ！

````
$ npx sls invoke local -f hello
````
次のようなレスポンスが帰ってきたらOKです。

````
{
    "statusCode": 200,
    "body": "{\"data\":[{\"DataType\":{\"S\":\"HUGA\"},\"ID\":{\"S\":\"HUGA_2\"}},{\"DataType\":{\"S\":\"Hoge\"},\"ID\":{\"S\":\"HOGE_1\"}}],\"event\":\"\"}"
}
````

# まとめ
今回は[Serverless DynamoDB Local]()を使ってDynamoDBのローカル開発環境を整えてみました。まとめると次のような感じでした。

1. `serverless create`でServerless Frameworkの環境構築
1. `npm install -D serverless-dynamodb-local`でnpmモジュールをインストール
1. `serverless.ts`にプラグインを記載して`npx sls dynamodb install`でServerless環境にインストール
1. `serverless.ts`に`resources`を追加し、`custum`にDynamoDB Localの設定を記載
1. `npx sls dynamodb start`で起動
1. 接続確認

少し手間がかかりますが、実際に立てられさえすればローカル開発がめちゃめちゃ捗ります！ぜひ使ってみてください。

次回はさらにS3 Bucketをローカル環境に追加してみます。ぜひこれからも最高のエンジニアライフを！！