Lambda function URL活用案件として、Lambdaだけを使ってアンケートフォームを作ってみた。 実際は回答の閲覧用にSlackも使ってるが、ほかは本当にLambdaだけ。
コード
冗長な部分もあるので、要点だけ抜粋。フルのコードはこちらに載せた。
これをLambdaのマネコンでデプロイし、Function URLを設定すれば良い。
また、 SLACK_WEBHOOK_URL
環境変数を設定すれば、アンケート結果をSlackに送信できる。
const https = require('https') exports.handler = async (event) => { console.log(event) const req = event.requestContext.http; const sourceIp = req.sourceIp; // can perform IP address restriction here // e.g. if (sourceIp != "11.4.51.4") throw new Error() if (req.method == 'GET') { if (!(req.path == '/')) { return { statusCode: 404, body: 'Not found', }; } return { statusCode: 200, body: getHTML(), headers: { 'content-type': 'text/html' } }; } else if (req.method == 'POST') { const response = JSON.parse(event.body); await processResponse(response); return { statusCode: 200, body: 'success', }; } return { statusCode: 400, body: 'Bad Request', } }; const processResponse = async (response) => { const webhookUrl = process.env.SLACK_WEBHOOK_URL; await post(webhookUrl, response); }; const post = async (url, data) => { // c.f. https://stackoverflow.com/questions/40537749/how-do-i-make-a-https-post-in-node-js-without-any-third-party-module } const getHTML = () => { return ` <!doctype html> <html> <head> <meta charset="UTF-8" /> <script src="https://cdn.tailwindcss.com"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> </head> <body> <div class="flex container mx-auto py-10"> <div class="grow"> <form id="myForm"> <div class="mb-6"> <label for="q1" class="block mb-2 text-sm"> Q1. 名前を教えて下さい。</label> <input type="text" id="q1" name="q1" placeholder="もつ太郎" required /> </div> <div> <button type="submit" > Submit </button> </div> </form> </div> </div> <script> $('#myForm').submit(function (event) { event.preventDefault(); var $form = $(this); var $button = $form.find('button'); $.ajax({ url: '/', type: 'POST', data: JSON.stringify( $form.serializeArray().reduce((json, { name, value }) => { json[name] = value; return json; }, {}), ), headers: { 'Content-Type': 'application/json', }, }); }); </script> </body> </html> `; }
Function URLをブラウザで開くと、以下のようなアンケートフォームが表示される。
送信すると、Slackに連携される。
基本的な機能が揃っていることはお分かりいただけただろうか。
ポイント
1. ホスティングが単一のLambdaで完結する
今回はLambdaをHTTPアクセス可能にするために、Lambda Function URLと用いている。Amazon API Gatewayは使っていないので、複雑なAWSリソースの設定は不要。この程度の要件であればLambda単体で十分サービス提供可能である。
このため、Lambdaを1つだけマネコンでデプロイすれば完結する単純さが利点となる。 何らかの事情でGoogleフォームなどサードパーティー製ツールを利用できない方は、検討してみてはいかが。
ちなみに今は結果をSlackに連携しているが、上記の processResponse
の実装を変えれば、任意の連携を実現可能。
例えばDynamoDBに保存したり、Amazon SNSに送信したりといったように。
2. index.js
1つで完結させるために
今回は完全手作業でのデプロイを想定し、可能な限りLambdaの構成をシンプルにしている。
例えば、ファイルが複数になると急激に手作業でのデプロイが面倒になる。
HTMLなどは別ファイルに分けたほうが本来見通しが良いが、getHTML
関数に押し込めている。
また、JavaScriptのライブラリを使うとバンドルが必要になり、これも単純な手作業ではデプロイできなくなる原因。 このため、バックエンドの実装はライブラリを一切使っていない。
フロントエンドについては CDN経由でライブラリの読み込みが可能なので、 DOM操作のために jQuery (初めて触った!) だけ使っている。jQuery、もう死んだものだと思ってたが、ここ1年リリースがなくたしかに死んでそうだった。
ReactなどモダンなフレームワークはWebpackに類するツールの利用を前提としている感があり、案外超Lightweightな用途では使いづらい気がした (preactもつらそう。) jQueryだけだと古風すぎるので、HTMLの色付け用にTailwindを使っている。
この方法はフォーム画面を作るためにHTML/CSSの知識が必須なので、若干ハードルの高い方法ではある。
3. セキュリティ面
Lambda Function URLは現状ビルトインの認証が IAM 認証しかないが、IAM認証 はブラウザからのアクセスでは使いづらい。
今回は認証なしでPublicアクセスを前提としている。 一応イベントからクライアントのIPアドレスを取れるので、IPアドレス制限程度であれば容易に実現できる。
exports.handler = async (event) => { const req = event.requestContext.http; const sourceIp = req.sourceIp; // 簡易なIPアドレス制限 if (sourceIp != "11.4.51.4") throw new Error()
Basic認証を実装したら便利そうだと思ったが、なぜか現状は使えないようだった。
(www-authenticate
ヘッダーが自動でDropされるような挙動を示す)
まとめ
Lambdaだけでアンケートフォームを作ってみた。
メリットは、
- インフラは管理不要
- 極限まで簡単な構築手順
- 画面は無限にカスタマイズ可能
何らかの理由でこの手のSaaSが使えないという方は、検討してみても良いかも。