38
31

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

API GatewayとLambdaとDynamoDBを使ってサーバレス・最短で登録フォームを作る

Posted at

API GatewayとかLambdaでサーバレスな話は既にたくさん記事はあるので、詳しくはそちらをご覧ください。
この記事は個人的な備忘録的に記載しています。

試したこと

API GatewayとAWS LambdaとDynamoDBだけを使って、コメント登録サイトを作ってみました。
構成は以下のような感じです。

serverless_arch.png

API GatewayにGETリクエストでまずは登録済みのコメント情報を取得してHTML表示をし、その中のFormからAPI GatewayにPOSTリクエストを送り、新たなコメントを新規登録するという流れです。
本来ならS3とか、他のサイト上にFormとか作って、API Gatewayを呼び出すという感じでしょうが、ちょっとAPI
Gatewayの機能のお試し用で作ったので、API Gatewayのマッピングテンプレートを使ってHTML生成までAPI Gateway側で完結させるようにしています。

出来上がりはこんな感じです。

投稿フォーム.png

では、以下、試したことをまとめておきます。

IAMロール作成

LambdaからDynamoDBにアクセスして操作する権限を持ったロールを作成します。
IAMの管理画面から、Lambdaサービスに紐付くロールを作成し、「AmazonDynamoDBFullAccess」ポリシーを適用しておきます。

qiita_iam_create1.png
qiita_iam_create2.png

データ格納先のDynamoDBを設定

以下の情報で新規にテーブルを作成します。

  • テーブル名: Sample
  • プライマリキー: timestamp (数値)

これだけで完了。

Lambda function作成

今回は、Python3.6でfunctionを作成。
作成したLambda functionは2つ。

  • SampleGetFunc: GETリクエスト受信時にDynamoDBからデータをScanして返す関数
  • SamplePostFunc: POSTリクエスト受信時にDynamoDBにデータをPutItemして、登録後にScanして返す関数

各処理には、先程作成したIAMロールを割り当てておきます。
処理内容はそれぞれ以下のような感じです。

SampleGetFunc
import datetime
import boto3
from boto3.session import Session

def lambda_handler(event, context):
    region = "ap-northeast-1"
    session = Session(
        region_name=region
    )
    dynamodb = session.resource('dynamodb')

    table = dynamodb.Table('Sample')

    scan_response = table.scan()
    scan_response['Items'] = sorted(scan_response['Items'], key=lambda x:x['timestamp'], reverse=True) # scanしたデータをtimestampで降順にソートしたデータを返すようにしています。
    return scan_response
SamplePostFunc
import datetime
import boto3
from boto3.session import Session

def lambda_handler(event, context):
    region = "ap-northeast-1"
    session = Session(
        region_name=region
    )
    dynamodb = session.resource('dynamodb')

    table = dynamodb.Table('Sample')

    put_response = table.put_item(
        Item = {
                'timestamp': int(datetime.datetime.now().timestamp()),
                'content': event['content'],
        }
    )
    scan_response = table.scan()
    scan_response['Items'] = sorted(scan_response['Items'], key=lambda x:x['timestamp'], reverse=True)
    return scan_response

SamplePostFuncは入力データとして以下のフォーマットで送付されてくることを前提としています。

{
    "content": "formで入力されたコメント"
}

各Lambda functionのトリガーとして、「API Gateway」を選択し、新しくAPI Gateway「SampleApi」を作っておきます。

API GatewayでAPI定義

ここからはAPI Gateway側の設定に入ります。
先程作成したSampleApiのリソース設定を行います。

/v1/sampleapiという階層でリソースを作成します。
このリソースに対して、GETとPOSTのメソッドを作成。
各メソッド作成時のセットアップで、「統合タイプ」をLambda関数を選択し、先程作成したLambda functionをそれぞれ紐付けておきます。

qiita_api_create2.png

これでまずベースの定義は完了。

API Gateway フローの設定(POSTデータの引き渡し加工&マッピングテンプレートで表示用のHTMLを生成)

ここからは、リクエストが来たあと、Lambdaにデータを引き渡す部分の設定と受信したデータ(JSON形式)をマッピングテンプレートを使ってHTMLに加工して戻すところの設定を行います。

マッピングテンプレートを使ったHTML生成

まず、GETリクエストが来たときにLambda function(SampleGetFunc)からの戻りのJSONデータをHTMLに変換してみます。

先程作成したGETメソッドの設定の中で、「統合レスポンス」の項目を選択します。
この中で、「本文マッピングテンプレート」を設定します。
Lambda functionからの戻りのjsonデータが来た場合、ここのマッピングテンプレートに指定した形式に変換してレスポンスを返す設定です。

qiita_api_get_create1.png

Content-Typeをapplication/jsonとし、以下のようなHTMLを返すよう設定します。

#set($inputRoot = $input.path('$'))
 
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script type="text/javascript">
        <!--javascriptのコード-->
        </script>
        <style type="text/css">
        <!--cssのコード-->
        </style>
    </head>
<body>
    <div class="base">
        <div class="title">運用課題を自由に投稿してください </div>

        <div class="caution">
            <span class=box-title>Caution</span>
            <p>注意書き</p>
        </div>
        <form method="POST" enctype="application/x-www-form-urlencoded" action="https://API URL/prod/v1/sampleapi">
            <textarea name="comment" placeholder="Type your issues...." rows=5></textarea>
            <input type=submit value="投稿">
        </form>

        <div class="title">最近投稿された運用課題</div>
        #foreach($elem in $inputRoot['Items'])
        <div class="issue">
            "$elem.content"
            <div class="timestamp">
                "$elem.timestamp"
            </div>
        </div>
        #end
    </div>
</body>
</html>

冒頭の1行で、Lambda functionから返されたjsonデータを取得しています。formの生成と、取得したjsonデータをforeachで取得件数分ループさせて、DynamoDBに登録されているtimestamp、contentの情報を出力させています。

さらに、以下の「メソッドレスポンス」の設定で、ユーザに応答を返す際のContent-Typeを定義します。HTMLと認識してほしいので「text/html」としておきます。

qiita_api_get_create2.png

これでHTMLで返すための設定はOKです。

POSTデータ処理する設定

次に、POSTリクエストが来た際にリクエストデータを処理するための設定を行います。
POSTメソッドの設定の「統合リクエスト」を選択して、「本文マッピングテンプレート」
からformで送付されてきたリクエストのContent-Type「application/x-www-form-urlencoded」を受信したときの受信データ処理の内容を記載します。

{
#set( $tmpstr = $input.body )
#foreach( $keyandvaluestr in $tmpstr.split( '&' ) )
#set( $keyandvaluearray = $keyandvaluestr.split( '=' ) )
        "$keyandvaluearray[0]" : "$keyandvaluearray[1]"
#end
}

この辺りの対応はこちらの記事とか参考にさせてもらいました。

これで、POSTリクエストに含まれてくるデータをJSON形式に変換して、Lambdaに送付します。

設定は以上で完了です。
あとは、設定したAPIをdeployすれば実際に使えるようになります。

まとめ

少し強引ですが、API GatewayとLambdaでアプリケーション部分は完結でき、データはDynamoDBに格納するといったことを試してみました。静的コンテンツをちゃんと管理するならS3に配置するとかやらないとマッピングテンプレートの管理は面倒です。
とはいえ、API Gateway便利に色々活用できますね。

いつ止めるかわからないですが、作成したアプリはここで動かしてます。
匿名で不満とかを吐き出す口としてちょっと用意してみたのでよければ運用管理上の課題感とかつぶやいてみてください。
http://prt.nu/0/opsshare


38
31
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
38
31

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?