AWSサーバーレス連絡フォームの作り方💪(AWS Lambda、API Gateway、SES)

AWS S3にサイトを安くて簡単にホスティングできますが、S3は静的なサイトしかホスティングできないですので、従来ならバックエンド処理が必要となるフォーム送信の実装などは難しそうですね。しかし、クラウド時代ではそんな心配はありません！ソリューションはサーバーレスアーキテクチャです！AWSはすでに色々なサーバーレス構築のツールを提供しています。

今回はユーザからお問い合わせやフィードバックを送信するたびに、任意な宛先に通知メールを送信するシンプルなサーバーレスメールサービスを一緒に作ります。AWS LambdaとAPI Gatewayを利用し、簡単なAWSサーバーレス構築を紹介します。

構成図はこんな感じです。
![diagram.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/fe6fb6dc-e62e-4eaa-0a50-776a74b19ac4.png)


処理の流れとしては、ユーザーから連絡フォームより入力した情報を収集し、クライアント側のブラウザからAmazon API Gateway RESTfulサービスに投稿します。Amazon API Gatewayは、収集されたユーザー情報をAWS lambda関数に渡します。AWS Lambda関数は、Eメールを自動生成し、Amazon SESを使用してメールサーバーに転送します。


# 1.フォームを作成

- `index.html`ファイルを作成し、下記のコードを貼り付けます。

```html:index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>連絡フォーム</title>
    <script src="script.js"></script>
    <link rel="stylesheet " href="style.css ">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js "></script>

</head>


<body>
    <h1>連絡フォーム</h1>
    <form id="contact-form" method="post">
        <h4>名前:</h4>
        <input type="text" id="name-input" placeholder="お名前を入力してください。" class="form-control" /><br />
        <h4>件名:</h4>
        <input type="subject " id="subject " " placeholder="お名前を入力してください。 " class="form-control " /><br />
        <h4>メール:</h4>
        <input type="email " id="email-input " placeholder="メールを入力してください。 " class="form-control " /><br />
        <h4>メッセージ:</h4>
        <textarea id="description-input " rows="3 " placeholder="メッセージを入力してください。 " class="form-control "></textarea><br />
        <div class="button-wrapper "><button type="button " onClick="submitToAPI(event) " class="btn btn-lg ">送信</button></div>

    </form>
</body>
</html>
```

ブラウザでプレビューを確認できます。こんな感じです。
![basic-html-form.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/ca5b3765-6c36-0fe5-dcf3-c64eb083bcaf.png)

- 見た目はあまり良くないですので、改善しましょう。`style.css`を同じフォルダ内で作成し、下記のコードを貼り付けます。

```css:style.css

* {
    box-sizing: border-box;
}

body {
    text-align: center;
    color: rgb(255, 255, 255);
    background-color: #194680;
    padding-top: 20px;
}

#contact-form {
    margin: auto;
    text-align: left;
    padding: 20px;
    max-width: 430px;
    border: white solid 2px;
    border-radius: 10px;
}

button {
    text-align: center;
    margin-top: 20px;
    font-size: 18px;
    border: white solid 2px;
    background-color: white;
    border-radius: 5px;
}

.button-wrapper {
    text-align: center;
}

input,
textarea {
    width: 380px;
}

textarea {
    height: 200px;
    padding: 10px;
}

input {
    height: 35px;
}
```

ブラウザではこんな感じになります。
![styled-form.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/a2ebb2ed-5c20-1aba-664e-1ca29f928bab.png)



# 2. AWS Lambda関数の定義

- AWS Lambda画面で`新規関数を作成`し、`hello-world`のnodejsテンプレを選択します。
![lambda2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/7b30dfa1-d524-bb3c-ea61-2ae8b0bc1bc3.png)

- `基本的な情報`の画面では関数を任意に名付けて、`実行ロール`は最初選択のままで進みます。
![lambda3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/dc87a4ee-e8d5-c9aa-b3ea-6e19111bcaed.png)

- 次の画面の`関数コード`に下記のコードを切り替えます。

```javascript:index.js

var AWS = require('aws-sdk');
var ses = new AWS.SES();

//宛先のメールアドレスを入力してください。
var RECEIVER = 'example-receiver@gmail.com';

//送信先のメールアドレスを入力してください。
//送信先のメールはAWSに認証登録する必要があります。
var SENDER = 'example-sender@gmail.com';

//クライアントへのレスポンスヘッダーを設定
var response = {
    "isBase64Encoded": false,
    "headers": {
        "Content-Type": 'application/json',
        "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,Accept",
        "Access-Control-Allow-Methods": "POST,GET,OPTIONS",
        "Access-Control-Allow-Origin": "*",
    },
    "statusCode": 200,
    "body": "{\"result\": \"テスト成功です!\"}"
};

exports.handler = function(event, context, callback) {
    console.log('受理したイベント:', event);
    sendEmail(event, function(err, data) {
        context.done(err, null);
    });
    callback(null, response);
};

//メール送信処理
function sendEmail(event, done) {
    var params = {
        Destination: {
            ToAddresses: [
                RECEIVER
            ]
        },
        Message: {
            Body: {
                Text: {
                    Data: '● お名前: ' + event.name + '\n● 件名: ' + event.subject + '\n● メール: \n' + event.email + '\n● 内容: \n' + event.desc,
                    Charset: 'UTF-8'
                }
            },
            Subject: {
                Data: event.name + 'からの連絡フォームが来ました！',
                Charset: 'UTF-8'
            }
        },
        Source: SENDER
    };
    ses.sendEmail(params, done);
    return {
        response,
        body: JSON.stringify(params),
        statusCode: 200
    }
}
```

- 同じ`Lambda管理`画面で、上にスクロールし、`アクセス権限`のタブに切り替え、`ロール名`のリンクをクリックします。

![lambda-policy.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/c2e4a713-42f9-0850-4d1e-9fb5e5b707f3.png)

- 当関数のIAMポリシーをSES権限を追加します。IAM画面で該当ポリシーをクリックし`ポリシーを編集` ->`JSON`のフォーマットに切り替えし、下記のコードを追加します。

![lambda-policy3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/882c6a76-59c7-9d7a-e3b7-3c50f8ccfc80.png)


```
{
  "Sid": "FormPolicy",
  "Effect": "Allow",
  "Action": "ses:SendEmail",
  "Resource": "*"
}

```

こんな感じです。
![lambda-policy2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/3daf60b7-732d-e1bf-72f2-52ee91cdba78.png)


# 3. APIゲートウェイでAPI構築

- AWSメインコンソールよりAPI Gateway管理画面にアクセスし、`APIを作成`をクリックします。
![api.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/22e15677-0d92-25d5-bf02-71e2ea200c6b.png)

- 次の画面では`REST API`の`構築`をクリックします。

![api2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/d43afa37-4ecf-2010-087c-758d59ae6212.png)

- 次の画面はREST APIの選択のままで、API名前を任意に付けます。

![api2a.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/7c8d249d-897f-ac75-60ce-4e948aa7bd72.png)


- 次の画面は上にある`アクション`をクリックし、`メッソド作成`をクリック、`POST`を選択します。

![api3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/e368512f-6e9e-87c4-afd8-529832f9b62d.png)

- 右側の設定画面では、`統合タイプ`は`Lambda`で、`Lambda関数`は先ほど作成したものを選択します。

![api4b.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/9ba20260-fee5-f0bc-4315-89c051798bd0.png)

- CORSエラーにならないように、CORS機能も有効にします。`アクション`のドロップダウンを`CORS有効化`をクリックし、設定はそのままで保存します。

![api4c.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/d5ae98a2-7c6b-4020-ed69-72904d4282a3.png)

- API Gatewayの最新アップデートでは、ゲートウェイのレスポンスも設定しないと、CORSエラーが発生するため、設定しましょう。右の`ゲートウェイレスポンス` →　`DEFAULT 4XX` → `編集`の順番にナビゲートします

![api5a.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/e92fb603-1ce0-2915-b3d5-7873ef01f9f2.png)

- レスポンスヘッダーに下記の値を追加し、保存します。

```
  Access-Control-Allow-Origin: '*'
```

こんな感じです。
![api5b.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/c7e3b4bb-5f50-3a4c-ab43-07a32fe281a2.png)

これでAPIの構築は完了です。

# 4. SES送信用のメール検証登録
- スパームなどの悪用を防ぐため、AWSよりメール送信機能を利用するには、検証登録が必要です。下記の手順を従って、送信用のメールをAWSに検証登録します:

 - https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/verify-email-addresses-procedure.html

![mail.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/a8af0b7d-8788-e6a3-563c-1023f2e61574.png)

`verified`の状態になったら、OKです！

- Lambda画面に戻して、左上にある`テスト`で動作確認できます。![lambda-test.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/f1dd5e7b-73b3-ac86-5eb2-5cb146d58c99.png)

- サンプルテストJSONデータ:

```
{
  "name": "value1",
  "subject": "value2",
  "email": "test@test.com",
  "desc": "value3"
}
```

# 5.全コンポーネントを連携する
- 最後のステップではすべてを繋ぎます！`index.html`と同じいフォルダで`script.js`を作成し、下記のコードを貼り付け、`var URL`と`url`の2ヶ所に先ほど設定したAPI GatewayのエンドポイントURLを差し替えます。

```javascript:script.js

function submitToAPI(e) {
    e.preventDefault();
    //設定したAPI GatewayのエンドポイントURLをここに入れます。
    var URL = "API-GATEWAY-ENDPOINT-URL";

    //フォームの入力値をチェック
    var name = /[A-Za-z]{1}[A-Za-z]/;
    if (!name.test($("#name-input").val())) {
        alert("2文字以上記入してください。");
        return;
    }
    var subject = /[A-Za-z]{1}[A-Za-z]/;
    if (!subject.test($("#subject").val())) {
        alert("2文字以上記入してください。");
        return;
    }
    if ($("#email-input").val() == "") {
        alert("メールを入力してください。");
        return;
    }

    var email = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,6})?$/;
    if (!email.test($("#email-input").val())) {
        alert("メールアドレスは正しくありません。");
        return;
    }

    var name = $("#name-input").val();
    var subject = $("#subject").val();
    var email = $("#email-input").val();
    var desc = $("#description-input").val();
    var data = {
        name: name,
        subject: subject,
        email: email,
        desc: desc
    };

    $.ajax({
        type: "POST",
        //設定したAPI GatewayのエンドポイントURLをここに入れます。
        url: "API-GATEWAY-ENDPOINT-URL",
        dataType: "json",
        crossDomain: "true",
        contentType: "application/json; charset=utf-8",
        data: JSON.stringify(data),


        success: function() {
            // フォームをクリアし、送信成功のメッセージを表示する
            alert("メッセージが送信されました！");
            document.getElementById("contact-form").reset();
            location.reload();
        },
        error: function() {
            // 送信エラーのメッセージを表示する
            alert("メッセージ送信失敗！");
        }
    });
}

```

#6. 結論
フォームより受信するメールはこんな感じです。
![demo.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665134/23171857-48ab-fbf4-5d09-a2c865f3e695.png)


これでAmazon S3で静的Webサイトをホストしても、AWSアーキテクチャを利用し、ユーザよりサイト管理者へフォーム送信機能を実装できました！

初投稿です、、いかがでしょうか、、(´･ω･｀)
コメント、フィードバックなど大変モチベーションになりますので、
是非よろしくお願いいたします！
それでは、また今度！ (｀・∀・´)ﾉ