Asa Tech Blog

学んだことを備忘録として残しています

GPT4が作成してくれたAWS Serverless TODOアプリケーションを作成するチュートリアル

シンプルな TODO アプリチュートリアル

このチュートリアルでは、シンプルな TODO アプリケーションを作成します。技術要素としてバックエンドには AWS SAM, API Gateway, AWS Lambda, DynamoDB, Node.js を、フロントエンドには React.js を使用します。

目次

  1. 環境の準備
  2. バックエンドの構築
  3. フロントエンドの構築
  4. デプロイ

環境の準備

必要なソフトウェアのインストール

このチュートリアルでは、以下のソフトウェアがインストールされていることを前提にしています。

  • Node.js (v14 以上)
  • npm (v6 以上)
  • AWS CLI (v2 以上)
  • AWS SAM CLI (v1.0 以上)

これらのソフトウェアがインストールされていない場合は、公式ドキュメントに従ってインストールしてください。

AWS 設定

AWS CLI がインストールされていることを確認したら、次に AWS アカウントを設定します。ターミナルで以下のコマンドを実行してください。

aws configure

アクセスキー、シークレットアクセスキー、リージョン、出力形式を入力します。リージョンは、このチュートリアルで使用する AWS サービスが利用可能なリージョンを指定してください。例えば、us-east-1ap-northeast-1 などです。

バックエンドの構築

プロジェクトの初期化

まず、プロジェクト用のディレクトリを作成し、その中に移動します。

mkdir simple-todo-app
cd simple-todo-app

次に、AWS SAM CLI を使用してバックエンド用のプロジェクトを初期化します。

sam init --runtime nodejs14.x --name backend
cd backend

テンプレートファイルの編集

template.yaml ファイルを開き、以下のように編集してください。

Resources:
  TodosTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: Todos
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 5

  GetTodosFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: getTodos.handler
      Runtime: nodejs14.x
      Environment:
        Variables:
          TODOS_TABLE: !Ref TodosTable
      Events:
        GetTodosApi:
          Type: Api
          Properties:
            Path: /todos
            Method: GET

  CreateTodoFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: createTodo.handler
      Runtime: nodejs14.x
      Environment:
        Variables:
          TODOS_TABLE: !Ref TodosTable
      Events:
        CreateTodoApi:
          Type: Api
          Properties:
            Path: /todos
            Method: POST

  UpdateTodoFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: updateTodo.handler
      Runtime: nodejs14.x
      Environment:
        Variables:
          TODOS_TABLE: !Ref TodosTable
      Events:
        UpdateTodoApi:
          Type: Api
          Properties:
            Path: /todos/{id}
            Method: PUT

  DeleteTodoFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: deleteTodo.handler
      Runtime: nodejs14.x
      Environment:
        Variables:
          TODOS_TABLE: !Ref TodosTable
      Events:
        DeleteTodoApi:
          Type: Api
          Properties:
            Path: /todos/{id}
            Method: DELETE

Outputs:
  TodosApi:
    Description: API Gateway endpoint URL for the todos
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/todos/"
  TodosTable:
    Description: DynamoDB table for storing todos
    Value: !Ref TodosTable

これにより、DynamoDB テーブルの作成と、各エンドポイントに対応する Lambda 関数が定義されます。

Lambda 関数の実装

src ディレクトリに移動し、以下の JavaScript ファイルを作成して、それぞれの Lambda 関数のコードを実装します。

getTodos.js

const AWS = require("aws-sdk")
const dynamo = new AWS.DynamoDB.DocumentClient()

exports.handler = async (event) => {
  const params = {
    TableName: process.env.TODOS_TABLE,
  }

  const result = await dynamo.scan(params).promise()

  return {
    statusCode: 200,
    body: JSON.stringify(result.Items),
  }
}

createTodo.js

const AWS = require("aws-sdk")
const dynamo = new AWS.DynamoDB.DocumentClient()
const uuid = require("uuid")

exports.handler = async (event) => {
  const data = JSON.parse(event.body)

  const params = {
    TableName: process.env.TODOS_TABLE,
    Item: {
      id: uuid.v1(),
      title: data.title,
      details: data.details,
      dueDate: data.dueDate,
      completed: false,
    },
  }

  await dynamo.put(params).promise()

  return {
    statusCode: 200,
    body: JSON.stringify(params.Item),
  }
}

updateTodo.js

const AWS = require("aws-sdk")
const dynamo = new AWS.DynamoDB.DocumentClient()

exports.handler = async (event) => {
  const data = JSON.parse(event.body)
  const id = event.pathParameters.id

  const params = {
    TableName: process.env.TODOS_TABLE,
    Key: {
      id: id,
    },
    UpdateExpression:
      "SET title = :title, details = :details, dueDate = :dueDate, completed = :completed",
    ExpressionAttributeValues: {
      ":title": data.title,
      ":details": data.details,
      ":dueDate": data.dueDate,
      ":completed": data.completed,
    },
    ReturnValues: "ALL_NEW",
  }

  const result = await dynamo.update(params).promise()

  return {
    statusCode: 200,
    body: JSON.stringify(result.Attributes),
  }
}

deleteTodo.js

const AWS = require("aws-sdk")
const dynamo = new AWS.DynamoDB.DocumentClient()

exports.handler = async (event) => {
  const id = event.pathParameters.id

  const params = {
    TableName: process.env.TODOS_TABLE,
    Key: {
      id: id,
    },
  }

  await dynamo.delete(params).promise()

  return {
    statusCode: 200,
    body: JSON.stringify({message: "Todo deleted successfully"}),
  }
}

これでバックエンドの実装が完了しました。

フロントエンドの構築

React アプリの作成

プロジェクトのルートディレクトリに戻り、React のプロジェクトを作成します。

cd ..
npx create-react-app frontend

必要なパッケージのインストール

axios をインストールして、API と通信できるようにします。

cd frontend
npm install axios

コンポーネントの作成

src ディレクトリ内に、以下のコンポーネントを作成します。

TodoList.js

import React, {useState, useEffect} from "react"
import axios from "axios"

const TodoList = () => {
  const [todos, setTodos] = useState([])

  useEffect(() => {
    getTodos()
  }, [])

  const getTodos = async () => {
    const response = await axios.get("<API_ENDPOINT>")
    setTodos(response.data)
  }

  return (
    <div>
      {todos.map((todo) => (
        <div key={todo.id}>
          <h2>{todo.title}</h2>
          <p>{todo.details}</p>
          <p>{todo.dueDate}</p>
        </div>
      ))}
    </div>
  )
}

export default TodoList

注: <API_ENDPOINT> をバックエンドの API エンドポイントに置き換えてください。

TodoForm.js

import React, {useState} from "react"
import axios from "axios"

const TodoForm = () => {
  const [title, setTitle] = useState("")
  const [details, setDetails] = useState("")
  const [dueDate, setDueDate] = useState("")

  const handleSubmit = async (e) => {
    e.preventDefault()
    const newTodo = {
      title: title,
      details: details,
      dueDate: dueDate,
    }

    await axios.post("<API_ENDPOINT>", newTodo)

    setTitle("")
    setDetails("")
    setDueDate("")
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>Title:</label>
      <input
        type="text"
        value={title}
        onChange={(e) => setTitle(e.target.value)}
      />
      <label>Details:</label>
      <input
        type="text"
        value={details}
        onChange={(e) => setDetails(e.target.value)}
      />
      <label>Due Date:</label>
      <input
        type="date"
        value={dueDate}
        onChange={(e) => setDueDate(e.target.value)}
      />
      <button type="submit">Add Todo</button>
    </form>
  )
}

export default TodoForm

注: <API_ENDPOINT> をバックエンドの API エンドポイントに置き換えてください。

コンポーネントの組み込み

src/App.js を開いて、以下のように編集してください。

import React from "react"
import TodoList from "./TodoList"
import TodoForm from "./TodoForm"

function App() {
  return (
    <div>
      <h1>Simple Todo App</h1>
      <TodoForm />
      <TodoList />
    </div>
  )
}

export default App

これでフロントエンドの実装が完了しました。

デプロイ

バックエンドのデプロイ

バックエンドのディレクトリに移動し、以下のコマンドを実行して AWS にデプロイします。

cd backend
sam build
sam deploy --guided

デプロイが完了したら、API エンドポイントをメモしておいてください。

フロントエンドのデプロイ

フロントエンドのディレクトリに移動し、以下のコマンドを実行してビルドします。

cd ../frontend
npm run build

ビルドが完了したら、build ディレクトリを適当のウェブサーバーにアップロードしてデプロイします。例えば、Amazon S3 に静的ウェブサイトとしてホスティングすることができます。詳細については、公式ドキュメントを参照してください。

これでシンプルな TODO アプリケーションが完成しました。フロントエンドの URL にアクセスして、タスクの追加、表示、編集、削除、完了を試してみてください。

まとめ

このチュートリアルでは、AWS SAM, API Gateway, AWS Lambda, DynamoDB, Node.js を使用したバックエンドと、React.js を使用したフロントエンドで構成されるシンプルな TODO アプリケーションを作成しました。

これを参考に、さまざまな AWS サービスや React.js を使用して、より複雑なアプリケーションを開発することができます。また、このチュートリアルでは触れていませんが、セキュリティやパフォーマンスの向上、CI/CD の導入、エラーハンドリングなど、本番環境にデプロイする際には検討すべき要素がさらにあります。

最後に、このチュートリアルが初心者にもわかりやすく、ソースコードディレクトリ構造が理解しやすい形で提供されていることを目指して作成しました。今後も技術要素を学びながら、より良いアプリケーション開発に役立ててください。