AWS APIGateway/LambdaとJavascriptで簡易問い合わせサイトをつくる

#概要
問い合わせフォーム(javascript)　⇒　APIGateway　⇒　Lambda(Node.js)　⇒　Lambda(Node.js)　の流れで簡単な問い合わせサイトを作ります。
<img width="400" alt="contact_form.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/8c9745f3-cca8-7fdd-4570-196afed42436.png">
一応レスポンシブにします。
<img width="300" alt="contact_form2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/c230f2cc-ec12-5ba6-fe14-f6a52f9a63d4.png">
バリデーションもあります。
<img width="400" alt="contact_form3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/16fb12ea-75ae-16f1-2684-92d6d85192d2.png">
<img width="400" alt="contact_form4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/6240e467-c333-7697-63d7-5a8c784fa5b5.png">
#index.htmlのコーディング

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

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <link rel="stylesheet" href="style.css" />
  <title>問い合わせ</title>
</head>

<body>
  <div class="header">
    <a href="#" class="logo">Hoge Hoge Company</a>
    <a class="active" href="#">Home</a>
  </div>

  <main class="main-container">
    <h2>問い合わせフォーム</h2>
    <div class="container">
      <form id="form">
        <div class="row">
          <div class="col-20">
            <label for="name">名前</label>
          </div>
          <div class="col-80">
            <input type="text" id="name" name="name" placeholder="Your name.." />
          </div>
        </div>
        <div id="name-alert" class="alert-message" hidden>入力値が不正です。</div>
        <div class="row">
          <div class="col-20">
            <label for="email">E-Mail</label>
          </div>
          <div class="col-80">
            <input type="email" id="email" name="email" placeholder="Your E-Mail.." />
          </div>
        </div>
        <div id="email-alert" class="alert-message" hidden>入力値が不正です。</div>
        <div class="row">
          <div class="col-20">
            <label for="dept">所属</label>
          </div>
          <div class="col-80">
            <input type="text" id="dept" name="dept" placeholder="Ex: Example Co., Ltd." />
          </div>
        </div>
        <div id="dept-alert" class="alert-message" hidden>入力値が不正です。</div>
        <div class="row">
          <div class="col-20">
            <label for="body">内容</label>
          </div>
          <div class="col-80">
            <textarea id="body" name="body" placeholder="お気軽にお問い合わせください。" style="height:200px"></textarea>
          </div>
        </div>
        <div id="body-alert" class="alert-message" hidden>入力値が不正です。</div>
        <div class="row">
          <button id="submitBtn">送信</button>
        </div>
      </form>
    </div>

    <!-- The Modal -->
    <div id="myModal" class="modal">
      <!-- Modal content -->
      <div id="modal-content">
        <span class="close">&times;</span>
        <p id="result"></p>
        <p id="detail" style="font-size: small;"></p>
      </div>
    </div>
  </main>
  <script src="main.js" defer></script>
</body>
</html>
```

#CSSのコーディング

```CSS:style.css
* {
  margin: 0;
  padding: 0;
  font-family: "Hiragino Kaku Gothic Pro", "ヒラギノ角ゴ Pro W3", "メイリオ",
    Meiryo, "ＭＳ Ｐゴシック", sans-serif;
}

/* Style the header with a grey background and some padding */
.header {
  display: flex;
  flex-flow: row wrap;
  align-items: center;
  justify-content: space-between;
  background-color: #000000;
  padding: 20px 10px;
}

/* Style the header links */
.header a {
  color: #f2f2f2;
  text-align: center;
  padding: 12px;
  text-decoration: none;
  font-size: 18px;
  line-height: 25px;
  border-radius: 4px;
}

/* Style the logo link (notice that we set the same value of line-height and font-size to prevent the header to increase when the font gets bigger */
.header a.logo {
  font-size: 25px;
  font-weight: bold;
}

/* Change the background color on mouse-over */
.header a:hover {
  background-color: #ddd;
  color: black;
}

/* Style the active/current link*/
.header a.active {
  background-color: dodgerblue;
  color: white;
}

/* Add media queries for responsiveness - when the screen is 500px wide or less, stack the links on top of each other */
@media screen and (max-width: 500px) {
  .header {
    justify-content: center;
  }
  .header a {
    padding: 12px;
  }
}

.main-container {
  width: 90%;
  margin: auto;
}
.main-container h2 {
  margin-top: 12px;
}

/* Style inputs, select elements and textareas */
input,
textarea {
  width: 100%;
  padding: 12px;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
  resize: vertical;
}

.alert-red {
  border: #ff4500 2px solid;
}

.alert-green {
  border: #00ff7f 2px solid;
}

.alert-message {
  color: #ff4500;
  font-size: small;
  text-align: end;
  margin-bottom: 10px;
}

/* Style the label to display next to the inputs */
label {
  padding: 12px 12px 12px 0;
  display: inline-block;
}

/* Style the submit button */
#submitBtn {
  background-color: #4caf50;
  color: white;
  padding: 10px 18px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 15px;
  float: right;
}

/* Style the container */
.container {
  border-radius: 5px;
  background-color: #f2f2f2;
  padding: 20px;
}

/* Floating column for labels: 25% width */
.col-20 {
  float: left;
  width: 20%;
  margin-top: 6px;
}

/* Floating column for inputs: 75% width */
.col-80 {
  float: left;
  width: 80%;
  margin-top: 6px;
}

/* Clear floats after the columns */
.row:after {
  content: "";
  display: table;
  clear: both;
}

/* Responsive layout - when the screen is less than 600px wide, make the two columns stack on top of each other instead of next to each other */
@media screen and (max-width: 600px) {
  .col-20,
  .col-80,
  #submitBtn {
    width: 100%;
    margin-top: 0;
  }
}

/* The Modal (background) */
.modal {
  display: none; /* Hidden by default */
  position: fixed; /* Stay in place */
  z-index: 1; /* Sit on top */
  left: 0;
  top: 0;
  width: 100%; /* Full width */
  height: 100%; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(0, 0, 0); /* Fallback color */
  background-color: rgba(0, 0, 0, 0.4); /* Black w/ opacity */
}

/* Modal Content/Box */
#modal-content {
  background-color: #fefefe;
  margin: 15% auto; /* 15% from the top and centered */
  padding: 20px;
  width: 80%; /* Could be more or less, depending on screen size */
}

.request-loading {
  border: 1px solid #888;
}

.request-success {
  border: 1px solid #00ff7f;
}

.request-fail {
  border: 1px solid #ff4500;
}

/* The Close Button */
.close {
  color: #aaa;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.close:hover,
.close:focus {
  color: black;
  text-decoration: none;
  cursor: pointer;
}
```

#javascriptのコーディング
変数URLはAPIGatewayで発行してから実装します。

```javascript:main.js
const URL = 'https://'; // APIGatewayで作成したURL

const LOADING = '処理中...';
const RESULT_OK = 'リクエストを受け付けました。';
const RESULT_OK_DETAIL =
  "正常に処理が完了すると 'support@hogehoge.com' からメールが配信されます。";
const RESULT_NG = 'リクエストの受付に失敗しました';
const RESULT_NG_DETAIL =
  '大変申し訳ありません。担当者へ直接お問い合わせください。';
const DOMAIN = '@sample.com';
document.getElementById('email').value = DOMAIN;

(() => {
  const modal = document.getElementById('myModal');
  const modalContent = document.getElementById('modal-content');
  const sendBtn = document.getElementById('submitBtn');
  const span = document.getElementsByClassName('close')[0];
  const result = document.getElementById('result');
  const form = document.getElementById('form');
  const nameAlert = document.getElementById('name-alert');
  const emailAlert = document.getElementById('email-alert');
  const deptAlert = document.getElementById('dept-alert');
  const bodyAlert = document.getElementById('body-alert');
  const resultDetail = document.getElementById('detail');

  const inputValueClear = () => {
    form.name.value = '';
    form.name.setAttribute('class', '');
    form.email.value = DOMAIN;
    form.email.setAttribute('class', '');
    form.dept.value = '';
    form.dept.setAttribute('class', '');
    form.body.value = '';
    form.body.setAttribute('class', '');
    result.innerHTML = '';
  };

  // When the user clicks on <span> (x), close the modal
  span.onclick = () => {
    if (result.innerHTML !== LOADING) {
      modal.style.display = 'none';
      if (result.innerHTML === RESULT_OK) inputValueClear();
    }
  };

  // When the user clicks anywhere outside of the modal, close it
  window.onclick = event => {
    if (event.target === modal) {
      if (result.innerHTML !== LOADING) {
        modal.style.display = 'none';
        if (result.innerHTML === RESULT_OK) inputValueClear();
      }
    }
  };

  sendBtn.addEventListener('click', async event => {
    event.preventDefault();
    const name = form.name.value.trim();
    form.name.value = name;
    const email = form.email.value.trim();
    form.email.value = email;
    const dept = form.dept.value.trim();
    form.dept.value = dept;
    const body = form.body.value.trim();
    form.body.value = body;

    // validation check
    if (validation(name, email, dept, body)) return;

    result.innerHTML = LOADING;
    modalContent.setAttribute('class', 'request-loading');
    modal.style.display = 'block';
    const jsonData = JSON.stringify({ name, email, dept, body });

    try {
      const res = await fetch(URL, {
        method: 'POST',
        mode: 'cors',
        cache: 'no-cache',
        body: jsonData,
        headers: {
          'Content-Type': 'application/json; charset=utf-8'
        }
      });
      console.log('Response!!', res.status);
      modalContent.setAttribute('class', 'request-success');
      result.innerHTML = RESULT_OK;
      resultDetail.innerHTML = RESULT_OK_DETAIL;
    } catch (error) {
      console.log(error);
      modalContent.setAttribute('class', 'request-fail');
      result.innerHTML = RESULT_NG;
      resultDetail.innerHTML = RESULT_NG_DETAIL;
    }
  });

  const validation = (name, email, dept, body) => {
    let validationResult = false;
    if (!name.length) {
      form.name.setAttribute('class', 'alert-red');
      nameAlert.hidden = false;
      validationResult = true;
    } else {
      form.name.setAttribute('class', 'alert-green');
      nameAlert.hidden = true;
    }
    if (!email.length || !/^[^@]+@sample.com$/.test(email)) {
      form.email.setAttribute('class', 'alert-red');
      emailAlert.hidden = false;
      validationResult = true;
    } else {
      form.email.setAttribute('class', 'alert-green');
      emailAlert.hidden = true;
    }
    if (!dept.length) {
      form.dept.setAttribute('class', 'alert-red');
      deptAlert.hidden = false;
      validationResult = true;
    } else {
      form.dept.setAttribute('class', 'alert-green');
      deptAlert.hidden = true;
    }
    if (!body.length) {
      form.body.setAttribute('class', 'alert-red');
      bodyAlert.hidden = false;
      validationResult = true;
    } else {
      form.body.setAttribute('class', 'alert-green');
      bodyAlert.hidden = true;
    }
    return validationResult;
  };

  form.name.addEventListener('input', event => {
    const name = event.target.value.trim();
    if (!name) {
      event.target.setAttribute('class', 'alert-red');
      nameAlert.hidden = false;
    } else {
      event.target.setAttribute('class', 'alert-green');
      nameAlert.hidden = true;
    }
  });

  form.email.addEventListener('input', event => {
    const email = event.target.value.trim();
    event.target.value = email;
    if (!email || !/^[^@]+@sample.com$/.test(email)) {
      event.target.setAttribute('class', 'alert-red');
      emailAlert.hidden = false;
    } else {
      event.target.setAttribute('class', 'alert-green');
      emailAlert.hidden = true;
    }
  });

  form.dept.addEventListener('input', event => {
    const dept = event.target.value.trim();
    event.target.value = dept;
    if (!dept) {
      event.target.setAttribute('class', 'alert-red');
      deptAlert.hidden = false;
    } else {
      event.target.setAttribute('class', 'alert-green');
      deptAlert.hidden = true;
    }
  });

  form.body.addEventListener('input', event => {
    const body = event.target.value.trim();
    if (!body) {
      event.target.setAttribute('class', 'alert-red');
      bodyAlert.hidden = false;
    } else {
      event.target.setAttribute('class', 'alert-green');
      bodyAlert.hidden = true;
    }
  });
})();
```
#AWS SES(Simple Email Service)を設定
※ドメインはRoute53で取得済みの前提
※併せてACM(AWS Certificate Manager)で証明書を取得済み
※SESは送信のみの設定です。

1. AWSへログイン
2. SESのコンソールへ移動
3. リージョンはバージニア北部を選択
4. 左のナビゲーションから`Domains`を選択<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/4bd92d44-86c4-331f-fbbc-bd167c3da508.png)
5. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/532e5be0-1519-5019-7c14-17e6d222d0e3.png)　をクリック
6. ↓のように入力。自分のドメインとDKIM設定をチェックして、![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/10841115-71eb-5d71-ba85-1344ffcd018d.png)をクリック![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/960f8334-ec51-d618-2aaa-c89b82c13e0d.png)
7. 次のモーダルではCNAMEやTXTが表示され、登録しろと指示が出る。Route53を利用している場合は、このモーダル上でDNSの登録がすべて完了する。
8. こんな感じになればOK!（※現時点ではサンドボックス上での制限された利用が可能）<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/0cc708ec-eda0-9f42-37a9-8a9e1b647701.png)
9. 左のナビゲーションで`Email Addresses`を選択<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/d7790f7d-426c-02ce-2bf5-a6984058e43f.png)
10. `Veryfy a New Email Address`でサンドボックス上で利用できるメールアドレスを登録する。
11. 登録したメールアドレスを`Send a Test Email`で登録したドメインからメールが送られるかテストして正常な動作を確認

#最後に起動するLambdaを作成

1. Lambdaのコンソールページへ移動
2. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/d59c853a-5c1a-61ac-79e2-168fce3e6c49.png)　をクリック
3. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/05376d3e-6ca1-1833-1e35-e0559112250b.png)　を選択
4. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/ea9ecf1d-29b6-dbd6-d44d-ccfa693da380.png)　適当な関数名を入力
5. ランタイムは`Node.js 12.x`を選択
6. そのほかはデフォルトのままでOK
7. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/eae3c84d-217c-82a4-9231-dc9e6db1f711.png)　をクリック
8. 環境変数へ配信元のアドレスを設定<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/bffcf287-82b5-6739-ce27-3913fcc35b5b.png)
9. 実行ロールにはSESの権限を許可<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/9de56e3d-8ad1-49c3-2ee4-1b9536534213.png)
10. 関数コードへ↓のコードを実装

```javascript:index.js
'use strict'
const SES = require("aws-sdk/clients/ses");
const ses = new SES({ region: "us-east-1" }); // 米国東部（バージニア北部)
const FROM = process.env.FROM; // 環境変数から取得

exports.handler = async (event) => {
    console.log(event);
    const TO = [event.email];
    const params = {
        Destination: {
            ToAddresses: TO
        },
        Message: {
            Body: {
                Text: {
                    Data: [
                        event.dept + ' ' + event.name + ' 様',
                        ' ',
                        'Hoge Hoge Companyです。',
                        'お問い合わせしていただきありがとうございます。',
                        '下記の内容で承りました。',
                        ' ',
                        '[お問い合わせ内容]' + "\n" + event.body,
                    ].join("\n"),
                    Charset: "utf-8"
                }
            },
            Subject: {
                Data: '受付完了：Webからの問い合わせ',
                Charset: "utf-8"
            }
        },
        // From
        Source: FROM
    };

    const result = {statusCode: 200};
    try {
        const response = await ses.sendEmail(params).promise();
        console.log('Response: ', response);
        result.body = 'OK!!!!!';
    } catch (error) {
        console.log('Error: ', error);
        result.statusCode = error.code;
        result.body = error.message;
    }
    return result;
};
```
※`process.env`では環境変数で設定したキーと値が利用可能（暗号化も可能）
#最後に起動するLambdaのテスト

1. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/5fd1016d-2990-4e89-e659-4e8f429de14d.png) テストイベントの選択をクリック
2. ↓のようにテストを作成(※emailキーにはSESで登録したアドレスを記載すること)<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/d82ebb2a-25fb-77e6-50a1-c7fb61e5ae1a.png)
3. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/9aa800e1-81e8-9b7c-89a1-c435efc95ef7.png)　をクリックしてテスト実行
4. 成功となればOK。失敗ならログを確認。<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/85b975d8-988b-a22f-0ba1-9fa9c9957a94.png)
5. テストでemailキーに指定したアドレスにメールが配信されていればOK

#最初に起動するLambdaを作成

1. Lambdaのコンソールページへ移動
2. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/d59c853a-5c1a-61ac-79e2-168fce3e6c49.png)　をクリック
3. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/05376d3e-6ca1-1833-1e35-e0559112250b.png)　を選択
4. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/ea9ecf1d-29b6-dbd6-d44d-ccfa693da380.png)　適当な関数名を入力
5. ランタイムは`Node.js 12.x`を選択
6. そのほかはデフォルトのままでOK
7. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/eae3c84d-217c-82a4-9231-dc9e6db1f711.png)　をクリック
8. 環境変数へ配信元のアドレスと受付先のアドレスを入力<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/ee5b4ac9-2fd8-71e7-2ee4-b0867fb2eee0.png)
9. 実行ロールはSESとLambdaの権限を追加<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/fcf3dd96-60d9-d719-713d-358477e9caf3.png)
10. 関数コードへ↓のコードを実装

```js:index.js
'use strict'
const SES = require("aws-sdk/clients/ses");
const ses = new SES({ region: "us-east-1" }); // 米国東部（バージニア北部)
const TO = [process.env.TO]; // 環境変数からの値を取得
const FROM = process.env.FROM;

// 次のLambdaを起動するための設定
const Lambda = require("aws-sdk/clients/lambda");
const lambda = new Lambda({ region: "ap-northeast-1" });

exports.handler = async (event) => {
    const name = event.form.name;
    const email = event.form.email;
    const dept = event.form.dept;
    const body = event.form.body;
    const sesParams = {
        Destination: {
            ToAddresses: TO
        },
        Message: {
            Body: {
                Text: {
                    Data: [
                        '[名前] : ' + name,
                        '[メールアドレス] : ' + email,
                        '[所属部署] : ' + dept,
                        '[お問い合わせ] : ' + "\n" + body,
                    ].join("\n"),
                    Charset: "utf-8"
                }
            },
            Subject: {
                Data: 'Webからの問い合わせ',
                Charset: "utf-8"
            }
        },
        // From
        Source: FROM
    };

    // 次のLambdaに送るデータ
    const payload = { name, email, dept, body };
    console.log('payload: ', payload);
    const lambdaParams = {
        FunctionName: "inpuirySendMailForm_2nd", // 最後に起動するLambdaの名称
        InvocationType: "Event",
        Payload: JSON.stringify(payload)
    };

    const result = {statusCode: 200};
    try {
        const response = await ses.sendEmail(sesParams).promise();
        console.log('Response: ', response);
        const callLambda = await lambda.invoke(lambdaParams).promise();
        console.log("Lambda Response: ", callLambda);
        result.body = 'OK!!!!!';
    } catch (error) {
        console.log('Error: ', error);
        result.statusCode = error.code;
        result.body = error.message;
    }
    return result;

};
```

#最初に起動するLambdaのテスト（次のLambdaも起動する）

1. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/5fd1016d-2990-4e89-e659-4e8f429de14d.png) テストイベントの選択をクリック
2. ↓のようにテストを作成<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/fff66699-c667-6af1-1277-3ecede32ce88.png)
3. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/9aa800e1-81e8-9b7c-89a1-c435efc95ef7.png)　をクリックしてテスト実行
4. 成功となればOK。失敗ならログを確認。<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/85b975d8-988b-a22f-0ba1-9fa9c9957a94.png)
5. テストでemailキーに指定したアドレスに受付完了メールが配信され、環境変数でTOに指定した受付先アドレスへ問い合わせ内容が配信されていればOK

#APIGatewayの設定

1. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/dde80417-9810-6998-48b8-4b9ad44cf578.png)をクリック
2. `REST API`の`構築`を選択<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/1bfb599a-21ed-1a97-8e1f-8fb85effa37d.png)
3. `API名`と`説明`を入力して`APIの作成`をクリック（他はデフォルト）<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/380e9295-5802-5be5-2872-95bd9e5568a6.png)
4. アクション⇒リソースの作成を選択⇒リソース名を入力（リソースパスは自動入力）⇒CORSを有効化⇒リソースの作成をクリック
5. アクション⇒メソッドの作成⇒POSTを選択⇒セットアップで最初に起動するLambda関数名を入力⇒保存をクリック
6. 統合リクエストをクリック⇒マッピングテンプレートを追加⇒Content-Typeは`application/json`⇒テンプレートには↓を入力⇒保存をクリック

```json
{
    "form": {
        "name":  "$util.escapeJavaScript($input.path('$.name'))",
        "email": "$util.escapeJavaScript($input.path('$.email'))",
        "dept":  "$util.escapeJavaScript($input.path('$.dept'))",
        "body":  "$util.escapeJavaScript($input.path('$.body'))"
    }
}
```

##APIGatewayのテスト

1. テストを実行する<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/5a36240a-42bd-d454-34c8-aa44ddc5314e.png)
2. Lambdaが起動してメールが配信されればOK

##APIGatewayのステージを作成
今回はv1というステージを作成
##APIGatewayのデプロイ
リソース⇒アクション⇒APIのデプロイを選択⇒v1ステージを選択して⇒デプロイをクリック
##発行されたURLをjavascriptのURL変数へ割り当て
#全体の動作確認
問い合わせフォームへSESで登録したアドレスと必要事項を入力し、送信ボタンをクリックして動作を確認
#実用化に向けて

+ LambdaのSDK呼び出しは動作確認ができているバージョンが呼び出されるようにする必要あり。
+ SESの上限緩和申請が必要。
+ APIGatewayでリソースポリシーを指定して不要なアクセスを防ぐ

#最後に
かなり大雑把に書きました。
小規模利用なら十分かと思います。
役に立つかな？
計画性なく作ってしまったのでここからブラッシュアップ