💯

サーバーレスで高速なCRUD操作を実現!LambdaとDynamoDBの組み合わせ開発手順

2023/04/21に公開

この記事では、AWS LambdaとDynamoDBを使用して、サーバーレスで高速なCRUD操作を実現する方法について解説します。

以下の手順に従って、実際に開発を進めていきましょう。

  1. DynamoDBのテーブルを作成
  2. Lambda関数の作成
  3. 環境変数設定
  4. jsファイル作成
  5. "express.zip"をLambdaにアップロード
  6. 【関数URL】/items にアクセス
  7. Talend API Testerで「Lambda2DynamoDB.json」をインポート
  8. 【関数URL】をLambdaの関数URLに変更
  9. 各リクエストを送信

1. DynamoDBのテーブルを作成

AWSマネージドコンソールを開き、DynamoDBを作成します。画面での入力内容は以下の通りです。

  • テーブル名:TestTable
  • パーティションキー:id

2. Lambda関数の作成

AWSマネージドコンソールを開き、Lambda関数を作成します。

画面での入力内容は以下の通りです。

  • 関数名:Lambda2DynamoDB
  • 実行ロール:AWS ポリシーテンプレートから新しいロールを作成
  • ロール名:Lambda2DynamoDBRole
  • ☑ 関数 URL を有効化
  • 認証タイプ:NONE

3. 環境変数設定

設定 > 環境変数 > 編集 から環境変数「TABLE_NAME」を設定します。

画面での入力内容は以下の通りです。

  • キー:TABLE_NAME
  • 値:TestTable

4. jsファイル作成

ローカルで以下のソースコードファイルを作成します。
※ ソースコードはGitHubに公開しています。

https://github.com/ryomeblog/zenn/tree/master/source/83d007e4125a29

index.js
const express = require('express');
const bodyParser = require('body-parser');
const dotenv = require('dotenv');
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const {
  DynamoDBDocumentClient,
  ScanCommand,
  PutCommand,
  GetCommand,
  DeleteCommand,
  UpdateCommand
} = require('@aws-sdk/lib-dynamodb');
const serverless = require('serverless-http');

dotenv.config();

const app = express();
app.use(bodyParser.json());

const client = new DynamoDBClient({
  region: process.env.AWS_REGION,
});

const dynamoDB = DynamoDBDocumentClient.from(client);
const tableName = process.env.TABLE_NAME;

// 登録
app.post('/items', async (req, res) => {
  const params = {
    TableName: tableName,
    Item: req.body,
  };

  try {
    await dynamoDB.send(new PutCommand(params));
    res.json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 更新
app.put('/items/:id', async (req, res) => {
  const { id } = req.params;
  const { updateKey, updateValue } = req.body;

  const params = {
    TableName: tableName,
    Key: { id },
    UpdateExpression: `set ${updateKey} = :value`,
    ExpressionAttributeValues: {
      ':value': updateValue,
    },
    ReturnValues: 'UPDATED_NEW',
  };

  try {
    await dynamoDB.send(new UpdateCommand(params));
    res.json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 削除
app.delete('/items/:id', async (req, res) => {
  const { id } = req.params;

  const params = {
    TableName: tableName,
    Key: { id },
  };

  try {
    await dynamoDB.send(new DeleteCommand(params));
    res.json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 一覧取得
app.get('/items', async (req, res) => {
  const params = {
    TableName: tableName,
  };

  try {
    const data = await dynamoDB.send(new ScanCommand(params));
    res.json(data.Items);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 条件一致取得
app.get('/items/search', async (req, res) => {
  const { searchKey, searchValue } = req.query;

  const params = {
    TableName: tableName,
    FilterExpression: `#key = :value`,
    ExpressionAttributeNames: {
      '#key': searchKey,
    },
    ExpressionAttributeValues: {
      ':value': searchValue,
    },
  };

  try {
    const data = await dynamoDB.send(new ScanCommand(params));
    res.json(data.Items);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// ローカルでアプリケーションの起動
// const port = process.env.PORT || 3000;
// app.listen(port, () => {
//   console.log(`Server is running on port ${port}`);
// });

// Lambdaでアプリケーションの起動
module.exports.handler = serverless(app);
package.json
{
  "name": "express",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@aws-sdk/client-dynamodb": "^3.303.0",
    "@aws-sdk/lib-dynamodb": "^3.303.0",
    "body-parser": "^1.20.2",
    "dotenv": "^16.0.3",
    "express": "^4.18.2",
    "serverless-http": "^3.2.0"
  }
}

ソースコードファイル作成後、npm i コマンドを実行します。

※ 事前準備としてnode.jsのインストールが必要。詳しくはこちら

  • コマンド実行例
コマンド実行例
>npm i
updated 141 packages and audited 141 packages in 46.582s

8 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

フォルダ構成が以下のようになっていれば成功。

  • フォルダ構成
フォルダ構成
.
├── index.js
├── package.json
├── package-lock.json
└── node_modules
    └── ...省略

上記をZIPファイル化します。

Windowsの場合は、ファイルとフォルダを選択し、右クリック > 送る > 圧縮(zip形式)フォルダー を選択する。(ファイル名は express.zip

Macの場合は、ZIPコマンドが必要。(brew install zip でインストールが可能)

コマンド
zip -r express.zip .

5. "express.zip"をLambdaにアップロード

アップロード元 > .zipファイル > アップロード から「express.zip」ファイルを選択してアップロードします。

6. 【関数URL】/items にアクセス

【関数URL】/itemsにアクセスしてレスポンスを確認します。期待値は以下です。

レスポンス
[]

7. Talend API Testerで「Lambda2DynamoDB.json」をインポート

Talend API Testerを開き、Importから「Lambda2DynamoDB.json」を選択してインポートします。

※ Chrome拡張機能のTalend API Testerはこちら

8. 【関数URL】をLambdaの関数URLに変更

Talend API Testerの各URL部分に【関数URL】という文字列が含まれているのでそれをLambdaの関数URLに直します。

9. 各リクエストを送信

登録、更新、一覧取得、条件一致取得、削除の順にリクエストを実行します。

これにより、LambdaとDynamoDBを組み合わせたCRUD操作が正常に動作していることを確認できます。

  • 登録
レスポンス
{
"success": true
}
  • 更新
レスポンス
{
"success": true
}
  • 一覧取得
レスポンス
{
"id": "0001",
"val": "テスト"
}
  • 条件一致取得
レスポンス
{
"id": "0001",
"val": "テスト"
}
  • 削除
レスポンス
{
"success": true
}

GitHub

今回ブログで紹介したソースコードはGitHubに公開しています。

https://github.com/ryomeblog/zenn/tree/master/source/83d007e4125a29

GitHubで編集を提案

Discussion