1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AWS】Lambda + API Gateway + DynamoDBで酒豪判定アプリを開発してみる【前編】

Posted at

はじめに

叡智に富んだ読者の皆様、御機嫌よう。
フルスタックエンジニアにメガ進化したいすぎちゃんです!

ぶっちゃけ、リアルすぎちゃんの懐事情は険しくなってきつつありますw(仕事しろ)

そんな爪に火を灯すような生活を送る中でWeb開発を試みると、
どうしてもコスパとタイパを気にせざるを得ません。

先月、AWSのAurora ServerlessをDBとしてポートフォリオを作ろうと試みたのですが、何もしなくても月に2500円もぶっ飛んじゃうので泣く泣く閉鎖することになりました :cry: (お金に余裕があればまた公開するかも)

さて、今回の記事ではコスパとタイパの両立ができるサーバレスアーキテクチャを元にアプリケーションの作り方をレクチャーしたいと思います!

「サーバレス」とは
サーバー周りの構築・運用を一切気にする必要がないサービス形態のこと。もちろん月々のサーバー代のことも気にしなくて良い。従量課金制が一般的で、料金体系は柔軟で使った分だけ支払えばOK

今回は東大式スクリーニングテストを元に「酒豪判定アプリ」のハンズオンを発信していきます!

アプリケーションの全機能の作成過程を一つの記事にまとめると長くなっちゃうので、以下のように複数の記事に分けて解説していきます!

本ハンズオンの構成

  1. DynamoDBに置いている質問項目を画面に表示しよう!⇦here
  2. 結果を送って酒豪か下戸かを判定してもらおう!
  3. 酒豪度を他の人と比較しよう!

このシリーズでは、AWSのサービスを利用してサーバーレスなアプリケーションを構築する方法を学んでいきます。以下の手順で進めていく予定ですので、ぜひ参考にしてください!

使用技術

フロントエンド

  • node: v20.18.0
  • npm: 10.8.2
  • vue.ts: @vue/cli 5.0.8

環境作成はこちらの記事を参考にしてください :bow:

インフラ

  • DynamoDB:Key-Value型のデータベースのこと。RDSと違いスキーマの定義を予めしておく必要がない
  • Lambda:Web上で関数を実行できるサービス
  • Amazon API Gateway:REST API, WebSocket APIなどを簡単に実行&管理ができるサービス。それぞれのAPIに対してテストも可能

1. DynamoDBの設定

1-1. 基本設定

スクリーンショット 2024-10-15 17.07.24.png

テーブル名はquestionnaire_items, パーティションキーはq_num(データ型は数値)に設定してください!

その他はデフォルトのままで構いません。

1-2. データの投入

「返された項目」の右上にある「項目を作成」をクリックしてください。

そうすると以下のような画面が表示されます。

スクリーンショット 2024-10-15 17.15.09.png

以下がquestionnaire_itemsテーブルに設定するべき属性です。

statement:お酒を飲んだ時の症状
choice_1:「いつも出る」の点数
choice_2:「時々出る」の点数
choice_3:「出ない」の点数

項目の登録を設問の数だけ繰り返します。

スクリーンショット 2024-10-14 19.20.23.png

一つ目の項目の設定が終わればチェックボックスをクリックし、右上の「アクション」>「項目の複製」をタップしてください。

そうすると一から属性を定義する必要がなく、値を入力するだけで項目の設定が完了します。

2. Lambdaの設定

2-1. 基本設定

Lambdaの一覧画面の右上にあるオレンジ色の「関数を作成」をタップしてください。
関数の作成画面の最上部に表示されている3つのオプションの中から「設計図の使用」を選択して下さい。

スクリーンショット 2024-10-15 17.52.56.png

設計図の使用」を選択すると設計図名、関数名の設定欄が表示されます。

スクリーンショット 2024-10-14 19.47.10.png

設計図名のドロップダウンメニューでは「Create a microservice that interacts with a DDB table」を選択してください。今回は比較的シンプルなpython3.10でLambdaのコードを書いていきます。

スクリーンショット 2024-10-14 19.47.44.png

スクリーンショット 2024-10-14 19.49.40.png

実行ロールのブロックでは「AWSポリシーテンプレートから新しいロールを作成」を選択してください!

ロール名はわかりやすいようにDynamoDbReadAccessに設定して、ポリシーテンプレートは「シンプルなマイクロサービスのアクセス権限」を選択しましょう。

上記の手順を踏まないと、Lambdaを実行する際にDynamoへのアクセスが許可されず正しいコードを実行してもデータを取得できません。

2-2. 関数の作成

lambda_function.py
import boto3
import json

print('Loading function')
dynamo = boto3.client('dynamodb')


def respond(err, res=None):
    return {
        'statusCode': '400' if err else '200',
        'body': err.message if err else json.dumps(res),
        'headers': {
            'Content-Type': 'application/json',
        },
    }


def lambda_handler(event, context):
    try:
        # DynamoDBクライアントを作成
        dynamodb = boto3.resource('dynamodb')
        table = dynamodb.Table('questionnaire_items')
        
        # 全てのレコードを取得
        response = table.scan()

        items = response.get('Items', [])
        
         # 'q_num'で並べ替え(昇順)
        sorted_items = sorted(items, key=lambda x: x['q_num'])
        
        # もしデータが無い場合
        if not items:
            return {
                'statusCode': 404,
                'body': 'No data found'
            }

        # データを返す
        return {
            'statusCode': 200,
            'body':  sorted_items
        }
    except Exception as e:
        # エラーが発生した場合はエラーログを出力
        print(f'Error processing data: {e}')
        return {
            'statusCode': 500,
            'body': f'Error processing data: {str(e)}'
        }

上記のコードを用いればDynamoDBに追加した項目一覧を取得することができます!

boto3.resource('dynamodb'): DynamoDBクライアントを作成する

dynamodb.Table('questionnaire_items'): questionnaire_itemsテーブルを指定

table.scan(): questionnaire_itemsテーブルに登録されたデータを全て取得

table.scan()を実務で使うのはあまりお勧めできません。データ数が多くなればなるほど料金が嵩んでしまうからです。

2-3. 疎通確認

スクリーンショット 2024-10-16 11.30.10.png

2-2のコードをlambda_function.pyに貼り付けたら、コードが正常に動作するか確認しましょう!
青色に点滅する「Test」ボタンをタップしてください。(途中、ウィンドウが何かしら出てくると思いますが、今回は何も設定しなくても大丈夫です!)

スクリーンショット 2024-10-14 19.59.22.png

Execution resultに上記の画像のようにstatusCodeが200のレスポンスが表示されていれば、DynamoDBからデータが取得できてます!

3. API Gatewayの設定

3-1. APIの定義

API Gatewayの設定画面に遷移すると、APIタイプの選択が求められます。

  • HTTP API
  • WebSocket API
  • REST API
  • REST API プライベート

今回はVPC外からのアクセスが必要であること、ゲームのようにリアルタイム性も必要ないためREST APIを使っていきたいと思います。

スクリーンショット 2024-10-14 20.04.38.png

「REST API」とは?

REST API は、REST アーキテクチャの制約に従って、RESTful Web サービスとの対話を可能にする アプリケーション・プログラミング・インタフェース (API または Web API) です。REST (Representational State Transfer) は、コンピュータ・サイエンティストの Roy Fielding によって作成された API の構築方法を定義する仕様であり、REST 用に設計された REST API (または RESTful API) は軽量で高速であるため、IoT (モノのインターネット)、モバイル・アプリケーション開発、サーバーレス・コンピューティングなどの先進的なコンテキストに最適です。

引用:Red Hat

選択するとREST APIの作成画面に遷移します。

スクリーンショット 2024-10-14 20.05.36.png

ラジオボタンでは新しいAPIを選択し、API名はquestionnaire_itemsと設定してください。APIエンドポイントタイプはデフォルトのままで大丈夫です。

REST APIの作成に成功すれば、以下の画像のようなリソースの設定画面に遷移します。

スクリーンショット 2024-10-14 20.06.41.png

3-2. Lambda関数との紐付け

次に3-1で作成したAPI定義とlambda関数の紐付けについて解説します。
「メソッド」ブロックの右上に位置する「メソッドを作成」をクリックしてください。

スクリーンショット 2024-10-15 18.04.17.png

クリックするとメソッド作成画面が表示されます。

スクリーンショット 2024-10-14 20.07.39.png

今回はアンケート項目の一覧を取得するのでメソッドタイプはGETに設定して下さい。
統合タイプはLambda関数に設定したのち、赤枠で囲っているドロップダウンメニューにて2で作成したLambda関数を選択してください!

紐付けに成功すれば、Lambdaのメソッド詳細画面が以下のように表示されるはずです :smile:

スクリーンショット 2024-10-15 18.00.57.png

スロットリングの設定

スクリーンショット 2024-10-15 15.27.22.png

「いたずらでF5ボタンを連打されたり、悪意をもった誰かにDos攻撃を受けて高額請求がきちゃった…」という痛ましいことにならないよう、スロットリングの設定も念のため行ってください!

「スロットリング」とは
システムやアプリケーションに対して過剰なリクエストや処理の負荷を制限する仕組みのことです。過剰な負荷がかかると、サーバーがダウンしたり、全体のパフォーマンスが低下したりする可能性があるため、それを防ぐために使われます。

添付してる画像とは異なりますが、レート数は1、バースト数を100に設定しておけば、運悪く誰かに攻撃を喰らって請求書をみた時に卒倒してしまう事態は防げるだろうと思います!安全管理大事!

実際に東京オリンピックの公式サイトは4.5億回の攻撃を受けたという報告がありました。
API Gatewayでは100万リクエストあたり3.5ドル発生する(2024年10月現在のレートに換算すると500円)ので、これの450倍分の料金が発生することを考えると恐ろしいですね…。

(ざっと計算してみましたが20万円請求されるみたいです…吐血)

APIのデプロイ

APIのデプロイに成功すれば、下記のようなステージ詳細画面にアクセスできます。

スクリーンショット 2024-10-15 18.15.53.png

APIエンドポイントが発行されているので、それをコピーして試しにブラウザのアドレスバーにペーストしてみましょう!

https://<ユニークなAPI識別子>.execute-api.us-east-1.amazonaws.com/questionnaire_items

上手くいけばブラウザに以下のように表示されるはずです!

スクリーンショット 2024-10-15 18.20.02.png

成功したお方はおめでとうございます!
これでサーバレスなマイクロアーキテクチャの構築に成功しました :clap:

フロントエンドへのデータ読み込み

vue.js

それでは
Questionnaire.vue

Questionnaire.vue
<template>
  <div class="home" style="text-align:left; max-width:720px; margin:auto;">
    <div class="inputGroup mb-6">
      <div v-for="(qstItem, idx) in questionnaireItems" :key="idx" class="pb-4">
        <div class="questionHeader pb-2" style="text-align: left">
          {{qstItem.statement}}
        </div>
        <div class="inputAnswer row">
          <label class="answer col-4 px-2" :for="idx+'_a'">
            <input class="answer-option" type="radio" :name="'q-'+idx" value=1 :id="idx+'_a'" v-model="userAnswers[idx]">
            <span class="answer-text">いつも出る</span>
          </label>
          <label class="answer col-4 px-2" :for="idx+'_b'">
            <input class="answer-option" type="radio" :name="'q-'+idx" value=2 :id="idx+'_b'" v-model="userAnswers[idx]">
            <span class="answer-text">時々出る</span>
          </label>
          <label class="answer col-4 px-2" :for="idx+'_c'">
            <input class="answer-option" type="radio" :name="'q-'+idx" value=3 :id="idx+'_c'" v-model="userAnswers[idx]">
            <span class="answer-text">出ない</span>
          </label>
        </div>
      </div>
    </div>

    <button @click="submit">submit</button>
  </div>
</template>

<script lang="ts">
import { Options, Vue } from 'vue-class-component'
import axios from 'axios'

export default class Questionnaire extends Vue {
  public questionnaireItems: [] = []
  public userAnswers: [] = []

  public getQuestionnaireItems () {
    return axios.get('https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/questionnaire_items').then((res) => {
      this.questionnaireItems = res.data.body
    })
  }

  <!---省略--->

  mounted () {
    this.getQuestionnaireItems()
  }
}
</script>

スクリーンショット 2024-10-15 15.24.54.png

CORSエラーの解消

API Gatewayで特に何も設定していなければVPC外からの接続を拒否してしまい、ローカル端末からのリクエストができなくなってしまいます。

そこでCORSの有効化を行えば、VPC外に存在する端末からアクセスが可能になります!

APIのリソース画面に移動し、リソースの詳細の右側に位置する「CORSを有効化する」をタップしてください。
スクリーンショット 2024-10-15 18.34.30.png

タップすると以下のような設定画面が表示されます。

スクリーンショット 2024-10-15 18.35.31.png

ゲートウェイのレスポンスでは全てのチェックボックスにチェックを入れ、
Access-Control-Allow-Methodsでは「GET」に必ずチェックを入れてください。

その他の設定はデフォルトままにしておき画面下部の「保存」ボタンをタップします。

「保存」ボタンをタップすると新規でOPTIONSメソッドが追加されているのを確認できます。

スクリーンショット 2024-10-15 18.36.00.png

その後、画面右上の「APIをデプロイ」を選択し同じステージに再度デプロイすれば、端末からのAPIへのアクセスが許可されるようになります。

再び読み込みをかけたところ無事にDynamoDBから取得したデータが画面に表示されました!

スクリーンショット 2024-10-15 16.26.04.png

お疲れ様でした! :clap:

つぶやき

初めてTypeScriptを使ってみたけどEslintさんがドラマに出てくる姑みたいに細かい…
お小言は愛の鞭と受け取っておこう…(吐血)

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?