Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8922

AWS Lambda入門②(Node編)〜DynamoDBにアクセスする〜

$
0
0

概要

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コンソールにアクセスしてテーブルができていることを確認してみましょう

スクリーンショット 2020-03-03 0.38.26.png

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.jsmodule.export.getAllとしたのでhandler: handler.getAllを追加しました
  • environmenthandler.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コンソール上でデータを追加した上で叩いてみてください
    • あとでもいい人は次でデータ登録処理も追加するのでそのあとに取得できることは確認できます

スクリーンショット 2020-03-05 1.35.36.png

残りの関数を追加する

  • 同じ要領で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や周辺ライブラリを使うとローカルでも動作確認できるので環境周りもとても充実しています
  • けっこう長くなってしまいましたがここまで読んでいただきありがとうございました

Viewing all articles
Browse latest Browse all 8922

Trending Articles