IT

DynamoDB local入門する〜expressを利用したDB書き込みまで〜

学習の備忘です。


やること

Dockerを利用してローカル環境でNode.js + DynamoDBの簡単なメッセージ記録システムを構築する。

Docker起動後には以下ページで動作確認ができます。

  • メッセージ送信用ページ

http://localhost:8080

http://localhost:8001


フォルダ構造

dynamo_docker
├ docker-compose.yaml
├ dockerfile
└ main
 ├ app.js
 ├ createtable.js
 ├ config.json
 ├ package.json
 ├ public
   └ index.html

dockerfile

FROM node:20
WORKDIR /app
COPY ./main/ .
RUN npm install
EXPOSE 8080
CMD ["node", "app.js"] 

docker-compose.yaml

起動用のコマンド:docker-compose up -d 

起動した後には、後述する node createtable.js を実行しテーブルを作成します。

composeのタイミングでテーブル作成を実行すると、接続ができないことがあったので起動後に手動で対応する形式にしました。

version: "3.8"
services:
  dynamodb-local:
    command: "-jar DynamoDBLocal.jar -sharedDb -dbPath ./data"
    image: "amazon/dynamodb-local:latest"
    container_name: dynamodb
    ports:
      - "8000:8000"
    volumes:
      - "./docker/dynamodb:/home/dynamodblocal/data"
    working_dir: /home/dynamodblocal

  dynamodb-admin:
    image: aaronshaf/dynamodb-admin
    container_name: dynamodb-admin
    ports:
      - "8001:8001"
    environment:
      - DYNAMO_ENDPOINT=http://dynamodb-local:8000

  app-node:
    build: .
    container_name: node
    depends_on:
      - "dynamodb-local"
    ports:
      - "8080:8080"

package.json

{
  "name": "express-app",
  "version": "1.0.0",
  "description": "Node.js Express Application",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "@aws-sdk/client-dynamodb": "^3.481.0",
    "@aws-sdk/lib-dynamodb": "^3.481.0",
    "express": "4.18.2"
  },
  "type": "module"
}

app.js

import express from "express";
import { join } from "path";
import pkg from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";
import config from "./config.json" assert { type: "json" };

const { DynamoDBClient } = pkg;

const app = express();
const port = 8080;

app.use(express.json());
app.use(express.static("public"));

const client = new DynamoDBClient(config.Dynamo);
const docClient = DynamoDBDocumentClient.from(client);

app.get("/", (req, res) => {
  res.sendFile(join(__dirname, "public", "index.html"));
});

app.post("/send", async (req, res) => {
  const messageText = req.body.message;
    const timestamp = new Date();
    timestamp.setHours(timestamp.getHours() + 9); 
    const jstTimestamp = timestamp.toISOString();

  try {
    await docClient.send(
      new PutCommand({
        TableName: "Messages",
        Item: {
          Timestamp: jstTimestamp,
          MessageText: messageText
        }
      })
    );
    res.status(200).send("Message sent successfully");
  } catch (error) {
    console.error("Error saving message:", error);
    res.status(500).send("Error saving message");
  }
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

createtable.js

import { CreateTableCommand, DescribeTableCommand, DynamoDBClient } from "@aws-sdk/client-dynamodb";
import config from './config.json' assert { type: 'json' };

const client = new DynamoDBClient(config.Dynamo);

async function main() {
    try {
        await client.send(new DescribeTableCommand({ TableName: "Messages" }));
        console.log("Table already exists.");
    } catch (error) {
        if (error.name === 'ResourceNotFoundException') {
            const createTableCommand = new CreateTableCommand({
                TableName: "Messages",
                AttributeDefinitions: [
                    {
                        AttributeName: "Timestamp",
                        AttributeType: "S"
                    }
                ],
                KeySchema: [
                    {
                        AttributeName: "Timestamp",
                        KeyType: "HASH",
                    },
                ],
                ProvisionedThroughput: {
                    ReadCapacityUnits: 1,
                    WriteCapacityUnits: 1,
                },
            });

            try {
                const response = await client.send(createTableCommand);
                console.log("Table created:", response);
            } catch (createError) {
                console.error("Error creating table:", createError);
            }
        } else {
            console.error("Error checking table existence:", error);
        }
    }
}

main();

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Message Sender</title>
</head>
<body>
    <h1>Send a Message</h1>
    <form id="messageForm">
        <input type="text" id="messageText" placeholder="Enter message" required>
        <button type="submit">Send</button>
    </form>

    <script>
        document.getElementById("messageForm").onsubmit = async function(event) {
            event.preventDefault();
            const message = document.getElementById("messageText").value;
            const response = await fetch('/send', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ message: message })
            });
            if (response.ok) {
                console.log("Message sent!");
                document.getElementById("messageText").value = ''; 
            } else {
                console.error("Failed to send message.");
            }
        };
    </script>
</body>
</html>

config.json

DynamoDBlocalはエンドポイント以外の情報は適当な値で大丈夫です。

{
  "Dynamo": {
    "region": "us-west-2",
    "credentials": {
      "accessKeyId": "dummy",
      "secretAccessKey": "dummy"
    },
    "endpoint": "http://dynamodb-local:8000"
  }
}

簡易動作イメージ

テキストボックスに文字列を入力し、Sendボタンを押すと、DynamoDBlocalに送信時刻と内容が記録されます。


参考ドキュメント


以上どなたかのお役に立てば幸いです。

  • この記事を書いた人

緑川縁

ニートからシステムエンジニアになった人
クラウド案件をメインにやっています。
保持資格:CCNA,AWS SAA&SAP,秘書検定2級
趣味でボカロ曲作り始めました。

-IT
-,