かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Serverless でも DynamoDB を使いたい!

前回までのあらすじ

TypeScript な Express を aws-serverless-express で Serverless 化して AWS Lambda にデプロイしました。
データベースを使えるように AWS の DynamoDB に接続できるようにしてみたメモ。

serverless.yml に DynamoDB の設定を追加する

serverless.yml に DynamoDB の設定・IAM role の設定を追加しておけば、デプロイした際に自動的に AWS に DynamoDB のテーブルが作成され接続できるようになるようです。

DynamoDB の設定・IAM role の設定をそれぞれ別のファイルで作成して serverless.yml で読み込ませるようにします。

serverless.yml に設定ファイルを読み込ませる設定を追加する

serverless.yml

provider:
  stage: ${opt:stage, 'dev'}
  environment:
    NODE_ENV: ${self:provider.stage}
    # process.env.DYNAMODB_TABLE でテーブル名を取得できるようにする
    DYNAMODB_TABLE: TABLE_NAME-${opt:stage, self:provider.stage}
  # IAM role の設定を読み込む
  iamRoleStatements: ${file(./config/iam.yml)}

resources:
  # DynamoDB の設定を読み込み
  - ${file(./config/dynamodb.yml)}

DynamoDB の設定

./config/dynamodb.yml

Resources:
  MtDynamoDbTable:
    Type: 'AWS::DynamoDB::Table'
    DeletionPolicy: Retain
    Properties:
      AttributeDefinitions:
        -
          AttributeName: id
          AttributeType: S
      KeySchema:
        -
          AttributeName: id
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 1
        WriteCapacityUnits: 1
      TableName: ${self:provider.environment.DYNAMODB_TABLE}

この設定の基づき DynamoDB が作成される。
データベースのテーブル名は読み込み元の serverless.yml にある environment.DYNAMODB_TABLE から取得される。

IAM role の設定

./config/iam.yml

- Effect: Allow
  Action:
    - dynamodb:Query
    - dynamodb:Scan
    - dynamodb:GetItem
    - dynamodb:PutItem
    - dynamodb:UpdateItem
    - dynamodb:DeleteItem
  Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}"

DynamoDB を操作できる IAM role を設定

webpack で DYNAMODB_TABLE をExpress に渡す設定

webpack.EnvironmentPlugin を使うことで serverless.ymlprovider.environment の値を process.env にマージしてプログラムに渡すことが出来る

webpack.config.js

const webpack = require('webpack');
const slsw = require('serverless-webpack');

const enviroment =
  slsw.lib.serverless.service.provider.environment.NODE_ENV || 'dev';

module.exports = {
  entry: slsw.lib.entries,
  // …
  plugins: [
    new webpack.EnvironmentPlugin(
      slsw.lib.serverless.service.provider.environment,
    ),
  ],
  // …
}

cf. TypeScript な Express を aws-seveless-express で Serverless にして AWS Lambda にデプロイするまでの記録 - かもメモ

DynamoDB に接続するサンプルのエンドポイントを作成

aws-sdk をインストール

$ npm install --save aws-sdk

/src/app.ts

import express from 'express';
import * as AWS from 'aws-sdk';
import { v4 as uuid } from 'uuid';

const dynamoDB = new AWS.DynamoDB.DocumentClient({ apiVersion: '2012-08-10' });
const TABLE_NAME = process.env.DYNAMODB_TABLE || 'LOCAL_TABLE_NAME';

const app = express();
const router = express.Router();

// ...

router.get(
  '/scan',
  async (req: express.Request, res: express.Response): Promise<void> => {
    try {
      const items = await dynamoDB
        .scan({
          TableName: TABLE_NAME,
        })
        .promise();

      res.send(items);
    } catch (error) {
      res.status(400).send(error);
    }
  },
);

router.get(
    '/test',
  async (req: express.Request, res: express.Response): Promise<void> => {
    try {
      const item = {
        id: uuid(),
        param: 'test',
      };

      await dynamoDB
        .put({
          TableName: TABLE_NAME,
          Item: item,
        })
        .promise();

      res.send(item);
    } catch (error) {
      res.status(400).send(error);
    }
  },
);

// …

AWS に deploy

$ sls deploy --stage prod -v

デプロイが完了したら AWS コンソールで DynamoDB のテーブルが作成されていることを確認。
API Gateway で表示された http://エンドポイントのURL/test で DynamoDB へのデータ作成・ http://エンドポイントのURL/scan で DynamoDB に作成されたデータが表示されていれば OK

Local 環境での DynamoDB との共存

local環境は Docker で作成していました。
--stage prod の時は AWS の DynamoDB に接続して、--stage dev の時は Docker 上の DynamoDB に接続するようにします。

Docker 上の DynamoDB に接続するには設定が必要なので process.env.NODE_DEVprod で無ければ接続先を Docker 上の DynamoDB に向ける設定をすれば OK

/src/app.ts

import express from 'express';
import * as AWS from 'aws-sdk';
import { v4 as uuid } from 'uuid';

if (process.env.NODE_ENV !== 'prod') {
  const config = {
    endpoint: 'http://localhost:XXXX', // docker 上の DynamoDB のエンドポイントURL
    region: 'northeast-1',
  };
  AWS.config.update(config);
}

const dynamoDB = new AWS.DynamoDB.DocumentClient({ apiVersion: '2012-08-10' });

これで local 動作させる時は Docker の DynamoDB を使用できるようになりました!
₍ ᐢ. ̫ .ᐢ ₎👌 ヤッタネ

やっとアプリを開発できる下準備が出来ました…
後は画像を使いたい場合に S3 にアップロードする方法とかが必要になってきそうですが、それは追々…


[参考]

真夜中のダイナモ

真夜中のダイナモ