もふもふ技術部

IT技術系mofmofメディア

serverless framework 第3回 dynamodb crud作成

はじめに

記事についての

こちらの記事は連載企画の第 3 弾です! aws-sdk を利用した dynamodb での crud の作成についてです!

記事一覧

  1. セットアップ&チュートリアル
  2. cognito を利用した認証機能の作成
  3. dynamodb を利用した CRUD の作成 <-- 現在
  4. serverless-offline を利用したローカル環境の作成
  5. 応用的な使い方(cognito を利用した authorizer の作成)

dynamodb を利用した CRUD の作成

dynamodb のテーブル設計に

事前にこちらの記事に目を通しておくとわかりやすいです。 検索のために用いる GlobalSecondaryIndexesの概念がわかりやすく説明されています。 https://blog.usize-tech.com/table-design-for-amazon-dynamodb/

dynamodb のセットアップ

まずは、serverlessコマンドによりプロジェクトを作成します。

  1. dynamodb へのアクセス権限と環境変数の設定を追加します。
provider:
  ...
  environment: # lambda内で使用する環境変数を指定できる
    DYNAMODB_USER_TABLE: ${self:app}-userTable
  iamRoleStatements: # アクセス権限の設定
    - Effect: Allow
      Action:
        - dynamodb:DescribeTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
        - "cognito:*"
        - "apigateway:*"
      Resource:
        - "*"
  1. 操作を行うテーブルを作成します。 今回はUserTableを使用します。
resources:
  Resources:
    UserTable: # 作成するテーブル名
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${self:app}-userTable # 任意のテーブル名
        ProvisionedThroughput: # エラーが治る
          ReadCapacityUnits: 5
          WriteCapacityUnits: 5
        AttributeDefinitions: # 属性の定義(必須属性のみで良い)
          - AttributeName: email
            AttributeType: S
          - AttributeName: name
            AttributeType: S
        KeySchema: # primary keyの設定
          - AttributeName: email
            KeyType: HASH # Hash key 必須 (Hash key (+ Sort key)がprimary keyとなる)
        GlobalSecondaryIndexes: # 検索のために
          - IndexName: name-index # 任意のインデックス名
            KeySchema: # 検索に用いるprimary keyの設定
              - AttributeName: name 
                KeyType: HASH
            Projection:
              ProjectionType: ALL
            ProvisionedThroughput: # エラーが治る
              ReadCapacityUnits: 5
              WriteCapacityUnits: 5
  1. 今回使用する関数を定義します。 事前に定義しておくことで関数のみのデプロイを行えるようになります。(serverless deploy functions -f [関数名])

空の関数を作成

"use strict";

module.exports.createUser = async (event) => {};

module.exports.getUser = async (event) => {};

module.exports.getAllUser = async (event) => {};

module.exports.getUsers = async (event) => {};

module.exports.updateUser = async (event) => {};

module.exports.deleteUser = async (event) => {};

作成した関数を紐付け

functions:
  createUser:
    handler: handler.createUser
    events:
      - httpApi:
          path: /createUser
          method: post
  getUser:
    handler: handler.getUser
    events:
      - httpApi:
          path: /getUser
          method: get
  getAllUser:
    handler: handler.getAllUser
    events:
      - httpApi:
          path: /getAllUser
          method: get
  getUsers:
    handler: handler.getUsers
    events:
      - httpApi:
          path: /getUsers
          method: get
  updateUser:
    handler: handler.updateUser
    events:
      - httpApi:
          path: /updateUser
          method: post
  deleteUser:
    handler: handler.deleteUser
    events:
      - httpApi:
          path: /deleteUser
          method: delete
  1. ここまで行ったら一旦デプロイ
serverless deploy
  1. テーブルが正しく作られていることを AWS Console より確認 dynamodb のテーブルに新規テーブルが作成されていれば OK

Image from Gyazo

これでセットアップ終了

関数の構成

// レスポンスのbody
let body;
// レスポンスのステータスコード
let statusCode = 200;
const headers = {
  "Content-Type": "application/json",
};
// リクエストボディ
const requestBody = JSON.parse(event.body);

/**
 * 関数内での変更箇所
 */

try {
  // 処理の実行
  await documentClient.put(params).promise();
  body = { data: Item, message: "create user success" };
} catch (err) {
  // console.logの結果はcloudwatch上で確認ができる
  console.log({ err });
  // エラーがあった際にはステータスコードを更新
  statusCode = 400;
  body = { message: "create user failed" };
}

// 返り値はレスポンスとして送られる
return {
  statusCode,
  body: JSON.stringify(body),
  headers,
};

CREATE

データの作成(put)

createUserを編集

// 登録する値
const Item = {
  email: requestBody.email,
  name: requestBody.name,
  // 定義していない属性を追加することができる
  // age: 23
};
const params = {
  TableName: process.env.DYNAMODB_USER_TABLE,
  // attribute_not_existsは重複した値を防ぐことができる
  ConditionExpression: "attribute_not_exists(email)",
  Item,
};
// putによりデータの追加を行うことができる
await documentClient.put(params).promise();
body = { data: Item };
curl -XPOST \
-H 'Content-Type: application/json' \
-d '{ "email": "test@example.com", "name": "test" }' \
[生成されたエンドポイント]/createUser

READ

単一データ取得(get)

getUserを編集

const params = {
  TableName: process.env.DYNAMODB_USER_TABLE,
  Key: {
    // 更新したい項目をハッシュキー(及びソートキー)によって1つ指定
    email: requestBody.email,
  },
};
// getではプライマリーキーにより一件データを取得できる
const result = await documentClient.get(params).promise();
body = { data: result.Item };
curl -XGET \
-H 'Content-Type: application/json' \
-d '{ "email": "test@example.com" }' \
[生成されたエンドポイント]/getUser

一括データ取得(scan)

const params = {
  TableName: process.env.DYNAMODB_USER_TABLE,
};
// scanではテーブルに含まれる全件のデータを取得する
await documentClient.scan(params).promise();
body = { data: result.Items };
curl -XGET [生成されたエンドポイント]/getAllUser

検索データ取得(query)

const params = {
  TableName: process.env.DYNAMODB_USER_TABLE,
  IndexName: "name-index",
  ExpressionAttributeNames: { "#name": "name" },
  ExpressionAttributeValues: { ":val": requestBody.name },
  KeyConditionExpression: "#name = :val", // 検索条件 (部分一致検索はできない)
};

await documentClient.query(params).promise();
body = { data: result.Items };
curl -XGET \
-H 'Content-Type: application/json' \
-d '{ "name": "test" }' \
[生成されたエンドポイント]/getUsers

UPDATE

データの更新(update)

const requestBody = JSON.parse(event.body);
const Item = {
  email: requestBody.email,
  name: requestBody.name,
};
const params = {
  TableName: process.env.DYNAMODB_USER_TABLE,
  Key: {
    // 更新したい項目をハッシュキー(及びソートキー)によって1つ指定
    email: Item.email,
  },
  ExpressionAttributeNames: { "#name": "name" },
  ExpressionAttributeValues: { ":val": Item.name },
  UpdateExpression: "SET #name = :val", // 検索条件 (部分一致検索はできない)
};
await documentClient.update(params).promise();
body = { data: Item };
curl -XPOST \
-H 'Content-Type: application/json' \
-d '{ "email": "test@example.com", "name": "test mark2" }' \
[生成されたエンドポイント]/updateUser

DELETE

データの削除(delete)

const requestBody = JSON.parse(event.body);
const params = {
  TableName: process.env.DYNAMODB_USER_TABLE,
  Key: {
    // 更新したい項目をハッシュキー(及びソートキー)によって1つ指定
    email: requestBody.email,
  },
};
// deleteでは一件のデータを削除できる
await documentClient.delete(params).promise();
curl -XDELETE \
-H 'Content-Type: application/json' \
-d '{ "email": "test@example.com" }' \
[生成されたエンドポイント]/deleteUser

まとめ

今回はsdkを利用したdynamodbのcrudの作成方法について書いていきました。あまり細かいことは書かずに気軽に試せる程度にしました。そのうちより細かな補足もまとめたいなとおもます。


参考

https://www.serverless.com/ https://blog.usize-tech.com/table-design-for-amazon-dynamodb/ https://qiita.com/Fujimon_fn/items/66be7b807a8329496899

会社の紹介

株式会社 mofmof では一緒に働いてくれるエンジニアを募集しています。 興味のある方は是非こちらのページよりお越しください! https://www.mof-mof.co.jp/ https://www.mof-mof.co.jp/recruit/

次回は『serverless-offline を利用したローカル環境の作成』を予定しています。