静的な Web ページに API Gateway + Lambda + SES でお問い合わせフォームを設置する

会社の公式ページやイベントの告知ページなど、**静的ページなんだけど問い合わせフォームだけは欲しい** というケースは少なくないと思う。そういうときに問い合わせフォームのためだけに動的な Web サイトを構築するのは手間もサーバ代も無駄にかかってしまってコスパが悪い。

一番簡単なのは Google フォームを埋め込むことだが、見た目をカスタマイズできないので場合によっては使えない。そこで今回は **問い合わせ用のエンドポイントを API Gateway + Lambda で作り、Lambda から SES を使ってメールを送信する** ような仕組みを作る。

ちなみに API Gateway / Lambda / SES の概念や詳しい設定方法については記述しないので各自調べて欲しい。

# SES
省略。
今回は `example.com` からメールを送れるように設定してあるものとする。

# API Gateway
`Content-Type:application/json` でこんな感じの JSON を POST したときに

``` json
{
  "name": "fooさん",
  "email": "foo@example.com",
  "body": "ふぅー！"
}
```

こんな感じのイベントが Lambda に投げられるようにする。

``` json
{
  "request": {
    "sourceIp": "123.123.123.123",
    "userAgent": "TestBrowser"
  },
  "mail": {
    "from": "info+website@example.com",
    "to": "info@example.com",
    "subject": "お問い合わせ"
  },
  "data": {
    "name": "fooさん",
    "email": "foo@example.com",
    "body": "ふぅー！"
  }
}
```

リクエストのマッピングテンプレートはこんな感じになる。

``` json
{
    "request": {
        "sourceIp":  "$context.identity.sourceIp",
        "userAgent": "$context.identity.userAgent"
    },
    "mail": {
        "from":    "info+${stageVariables.from}@example.com",
        "to":      "${stageVariables.to}@example.com",
        "subject": "お問い合わせ"
    },
    "data": {
        "name":  "$util.escapeJavaScript($input.path('$.name'))",
        "email": "$util.escapeJavaScript($input.path('$.email'))",
        "body":  "$util.escapeJavaScript($input.path('$.body'))"
    }
}
```

ステージ変数で送信先を変えれるようにしておくとテストをするときなどに開発用のメアドにメールを飛ばせるので便利。

# Lambda Function
今回は Node.js 4.3 を使う。

Execution Role には SES からメールを送信できる権限を持った Role を指定する。そうすることで Credentials を指定しなくても SES からメールが送信できる。

Lambda と SES とで使うリージョンが違う場合は指定しないといけないので注意。

``` js
'use strict'
const AWS = require('aws-sdk');

exports.handler = (event, context, callback) => {
    const ses = new AWS.SES({ region: 'us-east-1' });
    
    const body = [
        '[IPアドレス] ' + event.request.sourceIp,
        '[UserAgent] ' + event.request.userAgent,
        '[名前] ' + event.data.name,
        '[メールアドレス] ' + event.data.email,
        '[お問い合わせ内容]' + "\n" + event.data.body,
    ].join("\n\n");

    const email = {
        Source: event.mail.from,
        Destination: { ToAddresses: [event.mail.to] },
        Message: {
            Body: { Text: { Data: body } },
            Subject: { Data: event.mail.subject },
        },
    };
    
    ses.sendEmail(email, callback);
};
```

# テストしてみる
API をデプロイしたら curl でテストしてみる。

``` bash
$ curl -XPOST https://{APIのURL}/inquiry -H 'Content-Type:application/json' -d '{"name":"fooさん","email":"foo@example.com","body":"ふぅー！"}'
{"ResponseMetadata":{"RequestId":"xxxxxxxx"},"MessageId":"xxxxxxxx"}
```

メールが届くことを確認できれば OK

# フォームを作る
あとは適当にフォームを作って Ajax で叩けばよい。

以下、jQuery を使って叩く雑なサンプル。

``` html
<form action='https://{APIのURL}/inquiry' id='inquiryForm'>
  <input type='text' name='name' placeholder='氏名' />
  <input type='text' name='email' placeholder='メールアドレス' />
  <textarea name='body' placeholder='お問い合わせ内容' />
  <input type='submit' value='送信' />
</form>
```

``` js
$('#inquiryForm').submit(function(event) {
  event.preventDefault();
  var form = event.target;
  var jqXHR = $.ajax(form.action, {
    type: 'post',
    contentType: 'application/json',
    data : JSON.stringify({
      name:  form.name.value,
      email: form.email.value,
      body:  form.body.value,
    })
  });
  jqXHR.done(function(data, textStatus, jqXHR) { /* 送信完了時の処理 */ });
  jqXHR.fail(function(jqXHR, textStatus, errorThrown) { /* 送信失敗時の処理 */ });
});
```