AWS Lambdaで静的サイトにメール送信フォームを作る簡単な方法

AWS Lambdaで静的サイトにメール送信フォームを作る簡単な方法

AWS Lambdaで静的サイトにメール送信フォームを作る簡単な方法

本稿では個人的に作ったLambda用メール送信スクリプトを使ってメール送信フォームを作る方法をご紹介する。手っ取り早く知りたい方はGitHubに公開したソースコードをご覧頂きたい。

今はgitさえ使えればGitHub PagesやNetlifyで無料で静的なサイトを公開できる。便利な世の中だ。静的サイトとはサーバサイドを含まない単純なサイトのこと。自分のホームページやMarkdownノートアプリのサイトも全部Netlifyで配信している。速いし安定していてすごく気に入っている。

Netlifyで運用しているMarkdownノートアプリのサイト https://www.inkdrop.app/

静的サイトはサーバサイドを含まないので、自前で動的にページを生成したりフォームを設置することが出来ない。でもちょこっとだけそういう事がしたいというユースケースがあると思う。例えばホームページに問い合わせフォームを設置するとか。問題は、わざわざそのためだけにNetlifyの有料機能を使ったりサーバを設置するのは大げさだし面倒という点。いつくるか分からない問い合わせのためだけに、ずっとサーバをスタンバイさせるのは無駄。

サーバレスアーキテクチャはこういう「ちょい足し用途」にとても適している。必要な時に必要なリソースだけ割り当ててサーバを動かせる。例えばAWS Lambdaは動作時間と割当リソースのサイズで料金が決まるので、待っている間は料金がかからない。

今回作った問い合わせフォーム

今回、AWS Lambdaを使って自分のホームページに問い合わせフォームを設置してみた。ソースコードをGitHubに公開したので、これを元にすればすぐにあなたもメール送信APIが作れる。AWS Lambdaを使ってみたいという人には良いチュートリアルになると思う。

APIのアーキテクチャ

Lambdaは処理ごとに “function” を作って呼び出して使う。functionそれ自体はAWS上で動く単なるスクリプト。node.jsやJava、C#、Pythonが動かせる。SES(Simple Email Service)はAWSが提供するメール送信サービス。後述するけど簡単にメールが送れるので、構える必要はない。Lambda functionからこのSESを通じてメールを送信する。ただし、Lambda functionはデフォルトでは外部に公開されないので、自身のサイトから呼び出すにはHTTPエンドポイントを付けてやる必要がある。そしてAPI Gatewayがそれを担う。以上のアーキテクチャの概略を上図に示した。

このLambda functionはAWS標準で提供されている方法だととても煩雑で管理しづらい。その問題を解決するためにapexというツールを使う。apexの概要は以下の記事が参考になるだろう。

apexをインストールする:

<span id="6d56" class="ri pi io re b gz rj rk m rl rm">curl https://raw.githubusercontent.com/apex/apex/master/install.sh | sh</span>

次に、AWSのアクセスキーが環境にセットアップ済みであることを確認する。apexは以下の環境変数を使用する。

  • AWS_ACCESS_KEY_ID — AWSアカウントアクセスキー
  • AWS_SECRET_ACCESS_KEY — AWSアカウントシークレットキー
  • AWS_REGION — AWSリージョン

もしまだされていなければ、こちらを参考に設定する。次に、apexプロジェクトの初期化を行う。今回使用するスクリプト群をgitでcloneして、その中で初期化する:

<span id="6bab" class="ri pi io re b gz rj rk m rl rm">git clone <a class="ag hb" href="mailto:git@github.com" rel="noopener ugc nofollow" target="_blank">git@github.com</a>:craftzdog/send-email-lambda.git<br></br>cd send-email-lambda<br></br>apex init<br></br>> Project name: send-email</span>

プロジェクト名は send-emailとする。するとapexがプロジェクト名に倣ってIAM roleなどを自動で作成してくれて、project.json というファイルが生成される。このファイルを以下のように編集する。

<span id="2f86" class="ri pi io re b gz rj rk m rl rm">{<br></br> "name": "send-email",<br></br> "description": "Simple email transmitter",<br></br> "memory": 128,<br></br> "timeout": 5,<br></br> "environment": {},<br></br> "runtime": "nodejs6.10",<br></br> "role": "<YOUR_IAM_ROLE>"<br></br>}</span>

これでLambdaを使う準備が出来た。

Lambdaを使う準備ができたら、次にメールを送るための準備をする。まずはAWS SESで使いたいメールアドレスを認証して登録する。以下のように、SESの管理画面で “Verify a New Email Address” というボタンを押下する。

使いたいメールアドレスの登録

AWSから認証メールが飛んでくるので、メール内に書かれているリンクを開けば登録が完了。これでSESからそのアドレスを使ってメールが送れるようになった。

次に、Lambda functionからそのアドレスで送るように設定する。 functions/submit/function.json というファイルを開いて以下のように編集する。

<span id="df6c" class="ri pi io re b gz rj rk m rl rm">{<br></br> "environment": {<br></br> "SES_REGION": "us-west-2",<br></br> "FROM_NAME": "Craftzdog Contact Form",<br></br> "FROM_EMAIL": "<YOUR_AUTOMATED_EMAIL_SENDER>",<br></br> "TO_EMAIL": "<EMAIL_TO_RECEIVE>"<br></br> }<br></br>}</span>

  • SES_REGION: SESを使うリージョン。Oregonならus-west-2。
  • FROM_NAME: 送り元の名前。 “俺の問い合わせフォーム” とか。
  • contact@example.com
  • TO_EMAIL: 問い合わせを受け取りたいメールアドレス

Lambdaを使って誰かに好き放題されないように、標準では最低限の権限しかapexのIAMロールに与えられていない。SESを使いたいので、その権限を与えるポリシーを追加する。

IAMの管理画面に行って、apexが作成したロールを見つける。以下の画像ではロールの名前が contact-form_lambda_function となっているけど、あなたは send-email_lambda_function となっているはず。

“Create Role Policy”というボタンを押して、以下ようなポリシーを作成する。名前は send-email_submit にする。

<span id="8931" class="ri pi io re b gz rj rk m rl rm">{<br></br> "Version": "2012-10-17",<br></br> "Statement": [<br></br> {<br></br> "Sid": "Stmt1504526549000",<br></br> "Effect": "Allow",<br></br> "Action": [<br></br> "ses:SendEmail"<br></br> ],<br></br> "Resource": [<br></br> "*"<br></br> ]<br></br> }<br></br> ]<br></br>}</span>

これで送信する準備が整った。早速送ってみよう。以下のコマンドで直接Lambda functionを呼び出せる。

<span id="f994" class="ri pi io re b gz rj rk m rl rm">echo -n '{ "subject": "hello", "body": "world" }' | apex invoke submit</span>

件名が “hello” 本文が “world” のメールがあなたの受信ボックスに届いたら成功。おめでとう!もし来なければ、以下のコマンドでログを確認する。

<span id="da91" class="ri pi io re b gz rj rk m rl rm">apex logs -f</span>

エラーメッセージを読んで、正しくロールにポリシーが適用されているか、AWSのリージョンを間違えていないか、送信元メールアドレスは正しくSESに認証されているかなどを確認する。

さて、Lambda functionが用意できたがこれをサイトから呼び出せるようにしたい。そのためにAPI GatewayでHTTPエンドポイントを作成する。日本語だとこのあたりが参考になる。

/submit に対する POST メソッドのリクエストを受け取って、 Lambda functionを呼び出すように設定する。

“Create API” でAPIを新規作成する。名前は “my-awesome-send-email-api”とか。

Resoueces セクションにて、 “Actions” -> “Create Resource” を選択して /submit リソースを作成する。この時、 “Enable API Gateway CORS” にチェックを入れる。

作成した /submit エンドポイントに対して、 POST メソッドを受け付けるようにする。 “Actions” -> “Create Method” でメソッドを作成する。

メソッドの設定

上図のようにLambda functionを呼び出すように設定する。

APIの作成結果

準備ができたら、APIをインターネットに公開する。 “Actions” -> “Deploy API” を選択して APIをデプロイする。デプロイの名前は “production” とか。

これで以下のように、APIを外から呼び出せるようになった。

デプロイ結果

画面上部に表示されている Invoke URL というやつがAPIのエントリーポイント。curlで以下のように呼び出せる。

<span id="bf80" class="ri pi io re b gz rj rk m rl rm">curl --request POST \<br></br> --url <a class="ag hb" href="https://qen9yylar9.execute-api.us-west-1.amazonaws.com/production/submit" rel="noopener ugc nofollow" target="_blank">https://******.execute-api.us-west-1.amazonaws.com/production/submit</a> \<br></br> --header 'content-type: application/json' \<br></br> --data '{<br></br> "subject": "Hello",<br></br> "body": "Hoge"<br></br>}'</span>

メールが届いたら成功。おめでとう!

いたずらで大量にメールが送られたりしないように、呼び出し回数を制限しておくことをお勧めする。上記の画面で、 “Enable throttling” にチェックを入れて、 “Rate” の欄に 1とか2 と入力しておけばOK。

これはお好きな方法でいいと思うけど、HTML5 標準の fetch 関数を使う方法を紹介する。以下のような関数を作ればいい:

<span id="f1a8" class="ri pi io re b gz rj rk m rl rm">export default function sendEmail (subject, body) {<br></br> return fetch('<a class="ag hb" href="https://qen9yylar9.execute-api.us-west-1.amazonaws.com/production/submit'" rel="noopener ugc nofollow" target="_blank">https://******.execute-api.us-west-1.amazonaws.com/production/submit'</a>, {<br></br> method: 'POST',<br></br> headers: {<br></br> 'Accept': 'application/json',<br></br> 'Content-Type': 'application/json'<br></br> },<br></br> body: JSON.stringify({ subject, body })<br></br> })<br></br>}</span>

これでサイトからメールが送れるようになった。やったね!あとは入力された情報から件名や本文をJSで生成して、この関数を叩けばOK。

今回紹介したようなメールフォームは、例えばお店や会社のシンプルなホームページとかに付け足すのに最適だと思う。賢く作ればサーバ代がほぼゼロで運用できる上に多少凝ったことも出来る。S3やDynamoDBと組み合わせればコメント欄も付けられる。DynamoDBもLambdaと同じで完全に従量課金制なので、ちょい足し利用にはもってこい。もしまた機会があればその方法も紹介したいと思う。参考になったらClapお願いします!

Netlifyはいつからかは分かりませんが、現在はフォームハンドリング機能を無料で提供しているようです。なんと、NetlifyのファウンダーのMathias Biilmannに英語版ブログのコメント欄で教えてもらいました:

Great guide to using lambda, these kind of cloud functions are a really great way to glue different services together without relying on a bloated, monolithic, dynamic backend.Small note, though, form handling on Netlify is completely free currently, so no need for a paid plan to enable it. You can basically get this whole setup + comment spam filtering, just by adding a netlify attribute to your HTML form :)

— Mathias Biilmann (強調は筆者)

メールフォームを設置するだけならもはやAWS Lambdaすら不要ですね。Google Formsの代替としてとても強力な選択肢と言えます。

作ってます: https://www.inkdrop.app/

Read more

Inkdrop v6 Canary版リリースしました — 新Markdownエディタやその他新機能盛り沢山

Inkdrop v6 Canary版リリースしました — 新Markdownエディタやその他新機能盛り沢山

Inkdrop v6.0.0 Canary版リリースしました — 新Markdownエディタやその他新機能盛り沢山 こんにちはTAKUYAです。 v6.0.0 の最初の Canary バージョンをリリースしました 😆✨ v6では、アプリのコア機能の改善がたくさん盛り込まれています! * リリースノート(英語): https://forum.inkdrop.app/t/inkdrop-desktop-v6-0-0-canary-1/5339 CodeMirror 6 ベースの新しいエディタ フローティングツールバー v5ではツールバーがエディタの上部に固定されており、使っていないときもスペースを占有していました。 v6では、テキストを選択したときだけ表示されるフローティングツールバーに変わりました。 GitHub Alerts 構文のサポート Alerts の構文が正しい色と左ボーダーでハイライトされるようになりました。 ネストされたアラートや引用にも対応しています。 また、アラートタイプの入力を支援する補完機能も追加されました。 スラッシュコマンド 空行で /

By Takuya Matsuyama
AIのお陰で最近辛かった個人開発がまた楽しくなった

AIのお陰で最近辛かった個人開発がまた楽しくなった

AIのお陰で最近辛かった個人開発がまた楽しくなった こんにちは、TAKUYAです。日本語ではお久しぶりです。僕はInkdropというプレーンテキストのMarkdownノートアプリを、デスクトップとモバイル向けにマルチプラットフォームで提供するSaaSとして、かれこれ9年にわたり開発運営しています。 最近、その開発にClaude Codeを導入しました。エージェンティックコーディングを可能にするCLIのAIツールです。 最初の試行は失敗に終わったものの、徐々に自分のワークフローに馴染ませることができました。そして先日、アプリ開発がまた「楽しい」と感じられるようになったのです。これは予想外でした。 本稿では、自分がエージェンティック・コーディングをワークフローに取り入れた方法と、それが個人開発への視点をどう変えたかを共有します。 * 翻訳元記事(英語): Agentic coding made programming fun again 自分のアプリに技術的負債が山ほどあった ご想像のとおり、9年も続くサービスをメンテするのは本当に大変です。 初期の頃は新機能の追加も簡単で

By Takuya Matsuyama
個人開発を7年以上続けて分かった技術選択のコツ

個人開発を7年以上続けて分かった技術選択のコツ

個人開発を7年以上続けて分かった技術選択のコツ InkdropというMarkdownノートアプリを作り続けて7年になる。 お陰さまでその売上でずっと生活できている。 これまで個人開発でどう継続していくかについて「ユーザの退会理由をあれこれ考えない」とか「アプリの売上目標を立てるのをやめました」とか、ビジネス面あるいはメンタル面からいろいろ書いてきた。 今回は、技術面にフォーカスして、どう継続して開発していくかについてシェアしたい。 TL;DR * 最初はとにかく最速でリリースする事を最優先する * 迷ったら「ときめく方」を選べ * 程よいところで切り上げて開発を進める * 使っているモジュールがdeprecatedされるなんてザラだと覚悟する * 古いから悪いとは限らない * シンプルにしていく * 老舗から継続の秘訣を学ぶ * 運ゲー要素は排除しきれない 最初はとにかく最速でリリースする事を目標に技術選定する 開発計画とビジネス計画は切っても切り離せない。 コーディングに傾倒するあまり完璧主義に陥って結局リリース出来ないまま頓挫してしまう個人開発者は多い

By Takuya Matsuyama
子育て中の個人開発者の一日

子育て中の個人開発者の一日

子育て中の個人開発者の一日 どうもTAKUYAです。 久しぶりに生活まわりの事を書きたい。自分はInkdropというMarkdownノートアプリを売って生きている。 子供も無事順調に成長しており、あと数ヶ月で3歳になるというところで、イヤイヤ期もやっと終わりが見えてきた。 生活パターンもなんとなく定着しつつあるので、ここで一旦どんなルーティンなのか書き出してみる。ちなみに当方今年で40歳。 平日の1日の流れ * 06:30 妻と子供起床、朝食 * 07:10–30 俺起床、朝食 * 07:40 布団を畳んで子供を着替えさせる。妻はその間に化粧や通勤の準備 * 08:00 ストレッチと軽い筋トレ(腕立て50回、スクワット100回) * 08:10 妻と子供を見送る。15分前後瞑想 * 08:30 散歩 * 09:00 作業開始(カフェまたは家) * 11:00 昼飯 * 12:00 ダラダラする * 12:30 作業再開(だいたい家)

By Takuya Matsuyama