🙌
serverless-expressからDynamoDBを呼び出す
serverless-expressを利用したLambdaからDyanamoDBを呼び出す方法についてです。
前回のserverless-expressでAPI GatewayからLambdaを実行するの記事の続きになります。
コード作成
まずはコードです。
DynamoDBに接続するにはaws-sdk
を利用します。
$ npm install aws-sdk
続いてコードを書いていきます。
今回は前回の記事で作成したrouter.jsにそのまま書いてしまいます。
routes/router.js
const express = require('express');
const router = express.Router();
const AWS = require("aws-sdk");
// ローカルで実行する際の設定
const dynamoOptions = process.env.NODE_ENV === "development" ? {
region: "localhost",
endpoint: "http://localhost:8000",
} : {};
const documentClient = new AWS.DynamoDB.DocumentClient(dynamoOptions);
router.get('/', (req, res) => {
res.json({message : 'Hello World!'});
});
/**
* GET: /users ユーザー一覧取得
*/
router.get('/users', (req, res) => {
documentClient.scan({
TableName: 'Users'
}).promise()
.then((result) => res.json(result))
.catch((e) => res.status(422).json({ errors: e }));
});
/**
* GET: /user/:userId ユーザー取得
* @param userId 取得対象ユーザーのIDを指定
*/
router.get('/user/:userId', (req, res) => {
// 本来はuserIdのvalidationチェックが必要
documentClient.get({
TableName: 'Users',
Key: {
id: parseInt(req.params.userId)
}
}).promise()
.then((result) => res.json(result))
.catch((e) => res.status(422).json({ errors: e }));
});
/**
* POST: /user ユーザー作成API
* @param {req.body} { id: id, name: 名前, age: 年齢 }
*/
router.post('/user', (req, res) => {
documentClient.put({
TableName: 'Users',
Item: req.body
}).promise()
.then((result) => res.json(result))
.catch((e) => res.status(422).json({ errors: e }));
});
module.exports = router;
app.jsにも設定を追加します。
(ようかんさん @inoue2002 から記事の内容の漏れを教えていただきました。ありがとうございました。)
app.js
const app = express();
const bodyParser = require('body-parser')
const router = require('./routes/router');
const cors = require('cors')
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
if (process.env.NODE_ENV === 'local') {
app.use(cors());
}
// システムエラー検知時の共通レスポンス
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({message: 'システムエラーが発生しました。'});
});
app.use('/', router);
module.exports = app;
これでコードはOKです。
続いてローカルで動作確認していきましょう。
ローカルにDynamoDBの環境構築
今回はローカル環境でDynamoDBを立ち上げるためにDockerを利用しました。
$ docker pull amazon/dynamodb-local
$ docker run -d --name dynamodb -p 8000:8000 amazon/dynamodb-local
これでlocalhost:8000にDynamoDBが立ち上がりました。
このままだとテーブルが作成されていない状態なので、テーブルを作成していきます。
今回はNode.jsでテーブル作成するコードを用意しました。
script/create-table.js
// ローカル環境のDynamoDBテーブル作成
const AWS = require("aws-sdk");
AWS.config.update({
region: "localhost",
endpoint: "http://localhost:8000",
});
const dynamoDB = new AWS.DynamoDB();
const params = {
TableName : "Users",
KeySchema: [
{ AttributeName: "id", KeyType: "HASH"}
],
AttributeDefinitions: [
{ AttributeName: "id", AttributeType: "N" }
],
ProvisionedThroughput: {
ReadCapacityUnits: 1,
WriteCapacityUnits: 1
}
}
dynamoDB.createTable(params, (err, data) => {
if (err) {
console.error("Unable to create table. Error JSON:", JSON.stringify(err, null, 2))
} else {
console.log("Created table. Table description JSON:", JSON.stringify(data, null, 2))
}
})
では実行します。
$ node script/create-table.js
Created table. Table description JSON: {
-- 省略
これでローカル環境で動作確認ができるようになりました。
$ npm start
$ curl -X POST localhost:3000/user/ -H "Content-Type: application/json" -d '{"id":1, "name":"taro", "age":21}'
{}
$ curl localhost:3000/users
{"Items":[{"name":"taro","age":21,"id":1}],"Count":1,"ScannedCount":1}
o$ curl localhost:3000/user/1
{"Item":{"name":"taro","age":21,"id":1}}
DynamoDB作成
では実際にDynamoDBを作成していきます。
前回作成したserverless.ymlにDynamoDBを作成するためのresourceを記載します。
serverless.yml
service: serverless-express-test
provider:
name: aws
runtime: nodejs12.x
region: ap-northeast-1
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource: 'arn:aws:dynamodb:${self:provider.region}:*:table/Users'
package:
exclude:
- .git/**
- test/**
- README.md
- script/**
functions:
serverlessTest:
handler: lambda.handler
events:
- http: ANY /
- http: 'ANY {proxy+}'
resources:
Resources:
users:
Type: AWS::DynamoDB::Table
Properties:
TableName: Users
AttributeDefinitions:
- AttributeName: id
AttributeType: N
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
ポイントは2つです。
-
provider
にLambdaからDynamoDBを操作するためのIAM設定を追加 -
resources
にDynamoDBのテーブル作成処理を追加
これであとはデプロイするのみです。
$ sls deploy
--省略
endpoints:
ANY - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev
ANY - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/{proxy+}
--省略
動作確認します。
$ curl -X POST https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/user -H "Content-Type: application/json" -d '{"id":2, "name":"Hanako", "age":22}'
{}
$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/users
{"Items":[{"id":2,"name":"Hanako","age":22}],"Count":1,"ScannedCount":1}
$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/user/2
{"Item":{"id":2,"name":"Hanako","age":22}}
これでOKです。
コードはこちらに配置してあります。
参考情報
AWS-SDK - DynamoDB
Serverless.yml - Reference
DockerHub - dynamodb-local
Discussion
非常に参考になる記事をありがとうございます。
こちらの環境で試してみた際に、ローカルでuserをDBに保存しようとpostした際にreq.bodyがundefindeになってしまいました。こちら(https://qiita.com/Saw-000/items/58d917857c40f2e11a3b)の記事を参考に、body-parser の設定を行うことで解決できましたので、参考までに共有させていただきます!
ようかんさん (@inoue2002)
ありがとうございます!
内容修正しました。