たくさん同時に送信されてくるデータを、AWSを使ってまとめてkintoneに登録してみる話 (後編)

公開日:2025-03-07

注意

本記事は情報提供を目的としており、本記事の内容は無保証、サポートの対象外です。
サポート窓口、問合せ窓口にご質問をいただいても対応いたしかねますのでご了承ください。

システム開発グループ テクニカルマネージャーの浅利です。

今日は前回の続きで、大量の同時登録がある場合に Amazon SQS を使って kintone にまとめて登録する方法の、実際の実装についてです。

対象:

kintone と AWS の知識が多少ある方🙇‍♂️

前回のおさらい

前回の記事では、Amazon SQS を利用してデータを複数件まとめてから、いっぺんにkintoneに登録する方法を説明しました。
今回は、そのWebアプリを実際に作ってみましょう、という話です。

  • フォーム送信時に呼ばれるLambdaではSQSに登録する
  • SQSのメッセージ複数件で別のLambdaがトリガー起動されるようにし、kintoneへ複数レコード登録を行う
  • 重複メッセージ除去のため、DynamoDBを使う

お題: フォーム上で入力して、kintoneにまとめて登録する Webアプリを、実際に作ってみる

今回は簡単なWebアプリを作成し、AWSのサービスを活用してデータをkintoneにまとめて登録してみます。

JavaScript や Lambda のコード例も記載していますが、簡単のためエラー処理等は省略していますので、ご注意ください。

1. Webページ

簡単な問合せページのWeb画面を、HTMLで用意しました。
タイトル・メールアドレス・問合せ内容の入力エリアと、登録ボタンがあるだけの画面です。

<!DOCTYPE html>
<html lang="ja">
 <head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="main.css">
  <script src="main.js"></script>
  <title>問い合わせ</title>
 </head>
 <body>
  <form id="myform">
   <h1>問合せフォーム</h1>
   <div>
    <h2>タイトル</h2>
    <input type="text" name="title"></input>

    <h2>メールアドレス</h2>
    <input type="email" name="mail"></input>  
  
    <h2>問合せ内容</h2>
    <textarea name="detail"></textarea>
   </div>

   <button type="button" onclick="callApi()">登録</input>
  </form>
 </body>
</html>

さらに、登録ボタンを押した時に実行されるJavaScriptのコードも用意しました。
後で説明する関数URLにフォームの入力内容を送って、成功なら「登録しました」とダイアログが出るだけのスクリプトです。

const URL = "登録Lambdaの関数URLをここに書く(後で説明)";

async function callApi() {
    const form = document.getElementById("myform");
    const title = form.elements["title"].value;
    const mail = form.elements["mail"].value;
    const detail = form.elements["detail"].value;
    
    const data = JSON.stringify({title, mail, detail});
    console.log(data);
    try {
        const response = await fetch(URL, {
            method: "POST",
            cache: "no-cache",
            body: data,
            headers: {"Content-Type": "application/json; charset=utf-8"}
        });
        
        console.log(response);
        alert("登録しました");

    } catch(err) {
        console.log(err);
        alert("登録失敗しました");
    }        
}

これともう一つ、ここでは説明しませんがスタイルシートのファイル main.css も作って、この3つをS3バケットにアップロードします。

そして、大量にアクセスがあることを考慮して、Amazon CloudFront 経由でこのS3上のWebページが表示できるよう設定しました。
(その設定内容についてはここでは詳しくは説明しません)

2. 登録ボタンで呼び出されるLambda

先ほどのWebページの登録ボタンで呼び出されるLambdaを用意します。
簡単のため、エラー等を考慮せずに書きました。

import json
import boto3

SQS_URL = "SQSのURLをここに書く"
sqs_client = boto3.client("sqs")

def lambda_handler(event, context):
    body = event.get("body")

    sqs_response = sqs_client.send_message(QueueUrl=SQS_URL, MessageBody=body)
    print(sqs_response)

    return {
        "statusCode": 200,
        "body": "SUCCESS!"
    }

このLambdaに対して、(API Gatewayでもいいのですが) 今回は「関数URL」を設定します。
さらに関数URL設定時に、「その他の設定」の項目の CORSを有効にし、許可ヘッダーを「content-type」、許可メソッドを「POST」としました。

関数URLの設定が完了すると発行されるURLを、1 の main.js の const URL = "~"; へ記載し、ボタンを押したときにこのLambdaが動くようにします。

※ 1つ注意点といて、(ここでは簡単のため処理を特に入れていませんが、)
例えば「問合せ内容」欄に非常に長いテキストを入れてしまうと、SQSのメッセージの上限サイズ(256KB)を超えエラーになってしまいます。
もし大きいデータを扱いたい場合は、データはSQS以外に入れ、SQSにはそのIDだけを送る、といった工夫が必要になってくると思います。

3. SQSトピックを用意し、2のLambdaから送れるようにする

次は、SQSトピック用意します。 前回書いたように「標準キュー」にする必要があります。

SQSトピックが用意できたら、そのURLを 2のLambdaの SQS_URL = "~" へ記載します。 さらに、LambdaのIAMロールにて、このSQSトピックに SendMessage ができるようポリシーを設定する必要があります。
こうすることで、2のLambdaからこのSQSトピックへメッセージが登録できるようになります。

4. メッセージ重複チェック用のDynamoDBテーブル

前回の記事でも説明した重複メッセージの除去のため、DynamoDB にテーブルを1つ用意します。

ここでは、テーブル名「MessageIds」、プライマリキー「messageId」としました。
次のLambdaで、このテーブルに条件付き書き込みをすることで、すでに登録済みならスキップするという判定に使用します。

なお今回はやりませんが、古いデータは必要ないためTTLを設定しても良いと思います。

5. SQSから起動しkintoneへの登録を行うLambda

3のSQSからトリガー起動されるLambdaを用意します。
こちらも、簡単のため、エラー等を考慮せずに書いています。
また、kintoneへ登録する処理は皆さんご存知だと思いますので、省略しています。

import json
import boto3
from botocore.exceptions import ClientError

dynamodb_client = boto3.client("dynamodb")
        
def lambda_handler(event, context):
    sqs_records = event.get("Records")
    if sqs_records:

        kintone_records = []
        for record in sqs_records:
            messageId = record["messageId"]
            data = json.loads(record["body"])

            # すでに登録済みでないか、messageId をチェック
            not_duplicated = register_messageId(messageId)
            if not_duplicated:
                # kintoneへ登録用の形式へ変換
                new_record = {
                    "title": { "value": data.get("title") },
                    "mail": { "value": data.get("mail") },
                    "detail": { "value": data.get("detail") }
                }
                kintone_records.append(new_record)
        
        print(f"まとめて登録: {len(kintone_records)}件")
        if kintone_records:
            ★(省略) kintone REST API の、POST /k/v1/records.json を使って、kintone_records をいっぺんに登録する★

    return {}


def register_messageId(messageId: str) -> bool:
    # messageIdの重複チェックを行うために、DynamoDB に登録済みか確認し、
    # 登録されてないなら登録して true を返す
    try:
        dynamodb_client.put_item(
            TableName="MessageIds",
            Item={"messageId": {"S": messageId}},
            ConditionExpression="attribute_not_exists(messageId)"
        return True
    except ClientError as client_error:
        if client_error.response['Error']['Code'] == "ConditionalCheckFailedException":
            # 条件エラー → False
            return False
        else:
            raise Exception("エラー")

また、先ほどと同じようにLambdaのIAMロールの権限設定も必要です。
このLambdaはSQSからトリガー起動されてないといけないため、ReceiveMessage、DeleteMessage、GetQueueAttributes を許可するようポリシーを設定してください。

そのあと、トリガーの設定にて、3のSQSトピックから起動できるように設定します。
その際の設定は、前回お話ししたように、「バッチサイズ:100」「バッチウィンドウ:例 60秒」等と複数のメッセージをまとめて実行されるようにします。必要なら「最大実行数」を2以上に設定してもいいかもしれません。

※ このように「バッチサイズ:100」「バッチウィンドウ:60秒」と設定したとしても、必ず100個もしくは60秒経ったらまとめてLambdaが起動するというわけではなく、最大ここまで、という設定のようです。
タイミングによってはもっと少ないメッセージ数でLambdaが起動します。

6. kintoneアプリ

Webフォームから入力した内容が最終的に反映されるkintoneアプリを用意します。

今回の「タイトル」「メールアドレス」「内容」のそれぞれのフィールドを用意するだけです。

kintoneアプリが用意できたら、5のLambdaの、kintoneへ登録する部分のアプリID等をこのアプリに登録されるよう書き換えてください。

以上で、ひととおり仕組みが出来上がりました。

実際に試してみる

実際にWebフォームから登録してみましょう。
(試す前に、まとめて登録されるさまの確認のために、バッチウィンドウを300秒(5分)のように伸ばしておくと確認がしやすいと思います)

連続して登録できるように、ブラウザのウィンドウをたくさん開いて今回のWebフォームをそれぞれで表示します。 それぞれのウィンドウでタイトル・メールアドレス・問合せ内容をあらかじめ入力した状態にしてから、「登録」ボタンをダダダッと連続で押します。

すると、5のLambdaのCloudWatch Logにて、複数個のレコードがいっぺんに登録されることを確認できました。
(1個になるときもあります)

まとめ

以上、前回の記事の内容を、実際に実装してみるという話でした。

前回もお伝えしましたが、この仕組みを応用したものが、 サイボウズ株式会社様 事例紹介 の kintone AWARD グランプリの投票システムとなっていますので、 ぜひそちらの記事もご覧ください。

アールスリーが業務改善・システム開発を支援する「キミノマホロ for kintone」

アールスリーでは業務改善・システム開発を行うサービスを「キミノマホロ for kintone」として提供しています。

今回書かせていただいた内容も、そのシステム開発のノウハウをもとにしています。

また キミノマホロ for kintone では、以下のようなワークショップを無料で開催していますので、ぜひ詳細をご覧ください。

投稿者プロフィール

アバター画像
淺利(あさり)
Webとか.NETとかスマホとかマイコンとかシーケンサーとか、いろいろやってるエンジニアです