概要
- AWS Lambda入門①(Node編)〜関数をデプロイして動かす〜の続編です
- 今回はLambdaからDynamoDBにアクセスしてデータを保存したり取得したりしてみます
- テーブルから全件取得、1件取得、1件登録の3つの関数を作成します
DynamoDBとは
- AWSが提供するマネージドなデータベースサービスです
- RDBとは異なりkey-value形式なドキュメントデータベースです
LambdaからDynamoDBにアクセスしてみる
DynamoDBの設定
- まずはDynamoDBを使うためにServerlesFrameworkの設定をします
DynamoDBのテーブル定義の設定
- DynamoDBはデータベースなのでテーブルの作成からはじめます
- これまでと同じようにこれもServerlessFrameworkの機能で行うことができます
- 今回はHelloテーブルを作ってみます
serverless.yml
を修正します- 見づらいのでデフォルトで記載されていたコメントアウトは全て削除しています
serverless.yml
service:sls-sample# AWS周りの設定provider:name:awsruntime:nodejs12.xregion:ap-northeast-1stage:devenvironment:DYNAMODB_TABLE:${self:service}-${self:provider.stage}iamRoleStatements:-Effect:AllowAction:-dynamodb:Query-dynamodb:Scan-dynamodb:GetItem-dynamodb:PutItem-dynamodb:UpdateItem-dynamodb:DeleteItemResource:'arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}*'# Lambdaの設定functions:hello:handler:handler.hello# DynamoDBの設定resources:Resources:Hello:Type:AWS::DynamoDB::TableProperties:TableName:${self:provider.environment.DYNAMODB_TABLE}-helloAttributeDefinitions:-AttributeName:idAttributeType:SKeySchema:-AttributeName:idKeyType:HASHProvisionedThroughput:ReadCapacityUnits:1WriteCapacityUnits:1
- 大きく分けて前段と後段の2つの定義を追加しています
serverless.yml
environment:DYNAMODB_TABLE:${self:service}-${self:provider.stage}iamRoleStatements:-Effect:AllowAction:-dynamodb:Query-dynamodb:Scan-dynamodb:GetItem-dynamodb:PutItem-dynamodb:UpdateItem-dynamodb:DeleteItemResource:'arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}*'
- environmentはファイル内で扱える変数のような感じです
- テーブル名につけるprefixが何度か登場することになるのでenvironmentに定義しています
self:service
は一行目のservice
の値でself:provider.stage
は7行目あたりのstage
の値
- iamRoleStatementsはLambdaを実行するユーザの権限にDynamoDBへのアクセス許可を追加しています
serverless.yml
resources:Resources:Hello:Type:AWS::DynamoDB::TableProperties:TableName:${self:provider.environment.DYNAMODB_TABLE}-helloAttributeDefinitions:-AttributeName:idAttributeType:SKeySchema:-AttributeName:idKeyType:HASHProvisionedThroughput:ReadCapacityUnits:1WriteCapacityUnits:1
- テーブルの定義をしています
- テーブル名は上で定義したprefixにつなげてhelloという名前にしています
テーブルを作成する
serverless.yml
に設定を追加した状態でデプロイすると自動でテーブルが作成されます
sls deploy
- Webコンソールにアクセスしてテーブルができていることを確認してみましょう
DynamoDBへのアクセス処理の実装
ライブラリの追加
- DynamoDBにアクセスするために必要なライブラリを追加します
npm i aws-sdk
# or
yarn add aws-sdk
DynamoDBへの接続処理
- 今回は以下の3つの処理を実装しようと思います
- 全量取得
- IDで検索して1件取得
- 1件登録
- 全部一気にやると量が多いのでまずは全量取得からいきます
handler.js
を修正します
handler.js
'use strict';// AWS SDKをimportconstAWS=require('aws-sdk');// DynamoDBにアクセスするためのクライアントの初期化constdynamo=newAWS.DynamoDB.DocumentClient();// 環境変数からテーブル名を取得(あとでserverless.ymlに設定します)consttableName=process.env.tableName;// 全量取得module.exports.getAll=async()=>{constparams={TableName:tableName,};try{// DynamoDBにscanでアクセスconstresult=awaitdynamo.scan(params).promise();// 正常に取得できたらその値を返すreturn{statusCode:200,body:JSON.stringify(result.Items),};}catch(error){// エラーが発生したらエラー情報を返すreturn{statusCode:error.statusCode,body:error.message,};}};module.exports.hello=asyncevent=>{return{statusCode:200,body:JSON.stringify({message:event}),};};
- 説明はだいたいコードのコメントに書いておきました
- 今回はテーブルの内容を全量取得するので
.scan()
を使いましたがDynamoDB Clientは以下のようなAPIを提供します
- 複数件取得
- scan: 全件取得
- query: 条件に該当した項目を全件取得
- 単項目操作
- get: 1件取得
- put: 1件置換
- update: 1件部分更新
- delete: 1件削除
- 新しく
module.export
を追加したのでserverless.yml
も修正します- functionの項目を修正します
serverless.yml
# ...省略functions:hello:handler:handler.hellogetAll:handler:handler.getAllenvironment:tableName:${self:provider.environment.DYNAMODB_TABLE}-hello# ...省略
handler.js
でmodule.export.getAll
としたのでhandler: handler.getAll
を追加しましたenvironment
はhandler.js
に対して環境変数としてテーブル名を渡しています
ローカルで動作確認
- Lambdaのコードができたので動かしてみます
- デプロイする前にローカルで動作確認しましょう
DynamoDBをローカルで動かすための設定
serverless-dynamodb-local
というDynamoDBをローカルで動かすライブラリがあるのでインストールします
yarn add -D serverless-dynamodb-local
- DynamoDBをローカルで動かすための設定も追加します
serverless.yml
の一番下に追加してください
serverless.yml
# ...省略plugins:-serverless-dynamodb-localcustom:dynamodb:stages:devstart:port:8082inMemory:truemigrate:trueseed:trueseed:hello:sources:-table:${self:provider.environment.DYNAMODB_TABLE}-hellosources:[./seeds/hello.json]
plugins
は先程インストールしたserverless-dynamodb-local
を使うことを宣言していますcustom.dynamodb
でローカルでDBを起動する時に使う設定をしていますport
は何でも大丈夫です(デフォルトは8000)seed
は事前に登録しておくテストデータの設定です- 登録するデータは
./seeds/hello.json
に定義しておきます
- 登録するデータは
- テストデータとして
seeds/hello.json
を作成します
seeds/hello.json
[{"id":"1","message":"Hello"},{"id":"2","message":"Hello!!!"},{"id":"3","message":"Hello World"}]
- ServerlessFrameworkを使ってDBをインストールしセットアップを完了させます
sls install dynamodb
Lambda関数にローカルDB用の処理を追加
- ローカルのDBを使うために
handler.js
に少し手を加えます
handler.js
'use strict';constAWS=require('aws-sdk');// 環境変数にLOCALが設定されていたらローカルDB用の設定を使う(portはymlで定義したものを設定)constoptions=process.env.LOCAL?{region:'localhost',endpoint:'http://localhost:8082'}:{};constdynamo=newAWS.DynamoDB.DocumentClient(options);// ...省略
ローカルでDBにアクセスする
- 準備が長くなりましたがいよいよアクセスしてみます
- まずはDBを起動します
sls dynamodb start
- 以下のようなログが出ればOKです
$ sls dynamodb start
Dynamodb Local Started, Visit: http://localhost:8082/shell
Serverless: DynamoDB - created table sls-sample-dev-hello
Seed running complete for table: sls-sample-dev-hello
- LambdaのgetAll関数を叩きます
LOCAL=true
は環境変数としてLOCALにtrueを設定しています(handler.jsでローカルのDBを見に行く判定で使っていたやつ)
LOCAL=true sls invoke local--function getAll
- 以下のようなログがでればOKです!
{"statusCode":200,"body":"[{\"message\":\"Hello\",\"id\":\"1\"},{\"message\":\"Hello!\",\"id\":\"2\"},{\"message\":\"Hello World\",\"id\":\"3\"}]"}
AWSにデプロイして動作確認
- ローカルで確認できたらAWSにデプロイしましょう
sls deploy
- コマンド一発でデプロイできて便利ですね
- serverlessコマンドでアクセスしてみましょう
sls invoke --function getAll
- 現状データがないのでデータは0件ですが200が返ってきていれば成功です
{"statusCode":200,"body":"[]"}
- この時点でデータがとれることを確認したい人はWebコンソール上でデータを追加した上で叩いてみてください
- あとでもいい人は次でデータ登録処理も追加するのでそのあとに取得できることは確認できます
残りの関数を追加する
- 同じ要領で1件取得と1件登録の処理を追加してみましょう
関数と設定の追加
handler.js
の一番下に関数を追加する
handler.js
// ...省略// 1件取得module.exports.get=asyncevent=>{// パラメータで渡されたidを取得const{id}=event;// 検索条件のidを指定constparams={TableName:tableName,Key:{id},};try{constresult=awaitdynamo.get(params).promise();return{statusCode:200,body:JSON.stringify(result.Item),};}catch(error){return{statusCode:error.statusCode,body:error.message,};}};// 1件登録module.exports.put=asyncevent=>{// 一意な値を作るためにタイムスタンプを取得constid=String(Date.now());const{message}=event;constparams={TableName:tableName,Item:{id,message},};try{constresult=awaitdynamo.put(params).promise();return{statusCode:200,body:JSON.stringify(result),};}catch(error){return{statusCode:error.statusCode,body:error.message,};}};
- 全件取得の時は
.scan()
を使いましたが1件取得は.get()
1件登録はput()
を使っています serverless.yml
のfunctionの項目に設定を追加します
servreless.yml
# ...省略functions:hello:handler:handler.hellogetAll:handler:handler.getAllenvironment:tableName:${self:provider.environment.DYNAMODB_TABLE}-helloget:handler:handler.getenvironment:tableName:${self:provider.environment.DYNAMODB_TABLE}-helloput:handler:handler.putenvironment:tableName:${self:provider.environment.DYNAMODB_TABLE}-hello
ローカルで動作確認
- 1件登録
LOCAL=true sls invoke local--function put --data'{"message": "Hello!!!!!"}'
- 以下のようなログが出ればOK
{"statusCode":200,"body":"{\"id\":\"1583340674414\",\"message\":\"Hello!!!!!\"}"}
- 1件取得
LOCAL=true sls invoke local--function get --data'{"id": "1"}'
- 以下のようなログが出ればOK
{"statusCode":200,"body":"{\"message\":\"Hello\",\"id\":\"1\"}"}
AWSで動作確認
- ServerlessFrameworkのコマンドでデプロイします
sls deploy
- 1件登録
sls invoke --function put --data'{"message": "Hello!!!!!"}'
- 以下のようなログが出ればOK
{"statusCode":200,"body":"{\"id\":\"1583340839287\",\"message\":\"Hello!!!!!\"}"}
- 1件取得
- 直前の1件登録で追加したデータのIDを指定してみましょう
sls invoke --function get --data'{"id": "1583340839287"}'
- 以下のようなログが出ればOK
{"statusCode":200,"body":"{\"id\":\"1583340839287\",\"message\":\"Hello!!!!!\"}"}
おまけ
- 今回はscanとgetとputを作成しましたがquery(検索条件を指定して複数件取得)の場合は以下のような感じになります
- パラメータで指定したIDに合致するレコードが複数ある場合に全部取得できるような感じです
module.exports.query=asyncevent=>{const{id}=event;constparams={TableName:tableName,KeyConditionExpression:'id = :id',ExpressionAttributeValues:{':id':id},};try{constresult=awaitdynamo.query(params).promise();return{statusCode:200,body:JSON.stringify(result.Items),};}catch(error){return{statusCode:error.statusCode,body:error.message,};}};
感想
- LambdaからDynamoDBへのアクセスはDynamoDB Clientを使うと扱いやすいですね
- ServerlessFrameworkや周辺ライブラリを使うとローカルでも動作確認できるので環境周りもとても充実しています
- けっこう長くなってしまいましたがここまで読んでいただきありがとうございました