【AWS】基礎的なウェブアプリの構築チュートリアルやってみた

#はじめに
[基本的なウェブアプリケーションを構築する](https://aws.amazon.com/jp/getting-started/hands-on/build-web-app-s3-lambda-api-gateway-dynamodb/?trk=gs_card)チュートリアルをやってみた。所要時間も35分ほどで初心者にもとっつきやすい内容だった。一部スクショが貼ってなかったり画面が異なる箇所があったので、備忘録として残したい。

完成系は以下の通り。
First NameとLast Nameを入力し、Call APIを押すと、「Hello from Lambda, First Name Last Name」が表示される。
![スクリーンショット 2020-08-14 19.25.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/5df82ce6-f344-4ee5-0d69-8190f3468b1e.png)

#手順
##S3で静的ウェブサイトを作成
※全て東京リージョンで行う
S3>「バケットを作成する」を選択する。
![スクリーンショット 2020-08-14 8.45.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/78aa85cc-0cbb-960c-00af-203b64e4ef04.png)
「バケット名」を入力する。他のバケット名と被らないように、一意のものとする。「次へ」を選択する。
![スクリーンショット 2020-08-14 19.46.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/5c7a88e1-4c46-8904-5731-8742b5d512a2.png)
下記のようにアクセス許可設定を変えて、警告に対してチェックボックスをオンにする。「次へ」を選択する。
![スクリーンショット 2020-08-14 8.48.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/46b55e87-8d27-ced3-1215-f609a45fdf94.png)
内容を確認して、「バケットを作成」を選択する。
![スクリーンショット 2020-08-14 8.49.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/65663a43-d6f6-9e3b-48df-12b11d6fa003.png)
新しいバケットを選択する。プロパティ>「Static website hosting」を選択する。
![スクリーンショット 2020-08-14 8.53.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/57960cfe-e33a-cfc9-c6e2-2c8d1e187c58.png)
「このバケットを使用してウェブサイトをホストする」を選択する。
下記のように項目を入力する。
インデックスドキュメント 「index.html」
エラードキュメント 「error.html」
入力したら、「保存」ボタンを選択する。
![スクリーンショット 2020-08-14 8.57.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/40fd9373-4a62-b2e9-1106-147b595661bd.png)

ローカルでエディタを開き、新しいファイルを作成し、次のHTMLを貼り付ける。

```
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Hello World</title>
</head>

<body>
    Hello World
</body>
</html>
```

ファイルを`index.html`として保存。

２つ目のファイルを作成し、次のテキストファイルを貼り付ける。

```
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Error Page</title>
</head>

<body>
    Error Page
</body>
</html>
```

２つ目のファイルを`error.html`として保存。

アップロードボタンを選択。
![スクリーンショット 2020-08-16 9.14.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/a4121dba-21e8-183a-1007-ed0de23088bd.png)

`index.html`と`error.html`のファイルを追加する。「次へ」の選択をする。
![スクリーンショット 2020-08-14 9.03.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/54be84d0-a02d-20d3-7bea-0222689c7b24.png)
「このオブジェクトへのパブリック読み取りアクセスを付与する」を選択する。「アップロード」ボタンを選択する。
![スクリーンショット 2020-08-14 9.04.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/e46497de-e86a-0458-8ccf-e0bd47bbae39.png)

リストから`index.html`を選択する。
![スクリーンショット 2020-08-14 9.05.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/c25ac062-2351-1e82-815e-2f165ef53140.png)
オブジェクトURLをクリックする。
![スクリーンショット 2020-08-14 9.05.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/85887cf5-e441-ddb7-7d19-0e2d17f01a1a.png)
`Hello World`が表示されるか確認する。
![スクリーンショット 2020-08-14 9.05.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/9f7c3e9b-49b1-6634-6cb2-1782beb37e8d.png)

##Lambdaでサーバーレス関数を構築
Lambda>関数の作成を選択する。
下記項目を入力する。
関数名　「HelloWorldFunction」
ランタイム　「Python 3.8」
「関数の作成」を選択する。
![スクリーンショット 2020-08-14 17.49.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/5b56f8e1-b0e0-9846-2c49-305f186c2d27.png)
作成されたら、関数コードを下記のコードに置き換える。上部の「保存」ボタンを選択する。

```
# import the JSON utility package since we will be working with a JSON object
import json
# define the handler function that the Lambda service will use an entry point
def lambda_handler(event, context):
# extract values from the event object we got from the Lambda service
    name = event['firstName'] +' '+ event['lastName']
# return a properly formatted JSON object
    return {
    'statusCode': 200,
    'body': json.dumps('Hello from Lambda, ' + name)
    }
```

![スクリーンショット 2020-08-14 17.52.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/e0de1b05-1f91-76d7-d8b8-2c5eeb2802e3.png)
上部の「テストイベントを選択」>「テストイベントの設定」を選択する。
![スクリーンショット 2020-08-14 17.53.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/0e59f099-1b72-0000-5219-70ce976d7537.png)
下記項目を入力する。
イベント名　「HelloWorldTestEvent」
jsonオブジェクト

```
{
"firstName": "Ada",
"lastName": "Lovelace"
}
```

「作成」ボタンを押す。
![スクリーンショット 2020-08-14 17.54.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/b4cae9aa-3163-c3ea-4706-54f73444cb01.png)

「テスト」ボタンを選択する。

![スクリーンショット 2020-08-14 17.54.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/7e391adf-b366-286c-0699-e3e6a47d58dc.png)
「実行結果: 成功」が表示される。
![スクリーンショット 2020-08-14 17.55.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/79a64c4e-79cb-4d19-191d-5c16b1736d1f.png)

##API Gatewayを使用してサーバーレス関数をデプロイ

「APIを作成」を選択する。
![スクリーンショット 2020-08-14 18.00.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/4cfad9dd-0b83-60c8-9453-0f3545839077.png)
REST APIボックスで「構築」ボタンを選択する。
![スクリーンショット 2020-08-14 18.00.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/819f225e-d92c-2e0c-bc24-9ee279f23d46.png)
下記項目を選択。
プロトコルの選択　「REST」
新しいAPIの作成　「新しいAPI」
API名　「HelloWorldAPI」
エンドポイントタイプ　「エッジ最適化」
「APIの作成」ボタンを選択。
![スクリーンショット 2020-08-14 18.01.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/423846cc-35a9-0591-1ea7-d8dd821b97a7.png)
リソース>アクション>メソッドの作成を選択。
![スクリーンショット 2020-08-14 18.01.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/a9baf3f6-d137-2c48-7556-2528347cb508.png)
「POST」を選択し、チェックマークを選択。
下記項目を選択。
統合タイプ　「Lambda 関数」
Lambda関数　「HelloWorldFunction」と入力
「保存」を選択。アクセス許可を知らせるメッセージでは「OK」を選択。
![スクリーンショット 2020-08-14 18.02.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/b39cb641-fd8e-7bb6-5897-775dadc290f0.png)
アクション>CORSの有効化を選択。
![スクリーンショット 2020-08-14 18.03.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/cf3f003b-ea0e-f7d6-64c1-1205c85b1322.png)
「POST」にチェックを入れる。「CORSを有効にして既存のCORSヘッダーを置換」を選択する。
![スクリーンショット 2020-08-14 18.04.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/ae235d57-be1f-91b8-49c4-7bafb9edb489.png)
「はい、既存の値を置き換えます」を選択。
![スクリーンショット 2020-08-14 18.04.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/5901cd41-5a14-ea9c-6825-942544787837.png)
アクション>APIのデプロイを選択。
![スクリーンショット 2020-08-14 18.05.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/c61e0379-18e2-c512-72f0-86d8736fa7dc.png)
APIのデプロイで、下記項目を入力。
デプロイされるステージ　「新しいステージ」
ステージ名　「dev」
「デプロイ」を選択する。
![スクリーンショット 2020-08-14 18.06.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/a3eb53c3-a477-dfca-91f4-9510d9fbd510.png)
「URLを呼び出す」の横にあるURLを、コピーして保存。（あとでURLが必要になります。）
![スクリーンショット 2020-08-14 18.07.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/437fa2f3-e292-5a9d-27b1-5de2c228eef0.png)
リソース>POST>小さな青い稲妻のアイコンを選択。
![スクリーンショット 2020-08-14 18.08.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/28b0c446-3470-f67b-7429-74ccf9eba303.png)
リクエスト本文に下記を入力。

```
{
    "firstName":"Grace",
    "lastName":"Hopper"
}
```

「テスト」ボタンを選択。

![スクリーンショット 2020-08-14 18.08.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/fd812136-0daa-76bd-faae-6e910df449a0.png)
`statusCode 200`が表示される。
![スクリーンショット 2020-08-14 18.08.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/d7803bb5-ad2c-8718-060c-79d05bb97551.png)


##DynamoDBテーブルを作成
DynamoDB>テーブルの作成を選択。

![スクリーンショット 2020-08-14 18.14.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/0e2dc9a4-837c-9788-9b34-5d776b2d15ef.png)
下記項目を入力
テーブル名 「HelloWorldDatabase」
プライマリキー 「ID」
作成ボタンを押す。
![スクリーンショット 2020-08-14 18.15.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/3daadb1b-55c6-a2b5-40b9-bb397597d5d6.png)
Amazonリソースネーム (ARN)をコピーする。（あとで必要になります。）
![スクリーンショット 2020-08-14 18.16.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/6fc33009-ab6c-cc70-8d79-58a438a01951.png)
Lambda>`HelloWorldFunction`を選択。
![スクリーンショット 2020-08-16 13.43.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/6a13f59a-0ba2-e30b-5f27-698df6a62c63.png)
アクセス権限タブを選択。ロール名を選択すると、新しいブラウザが開く。
![スクリーンショット 2020-08-14 18.18.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/7a392e5b-563f-667b-31cf-55c3f5e2ef30.png)
アクセス権限タブでインラインポリシーの追加を選択。
![スクリーンショット 2020-08-14 18.20.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/23d7d99f-495f-39ed-5dd7-068b2d2ca5ad.png)
JSONタブで下記項目を入力。YOUR-TABLE-ARNで先ほどコピーしたリソースネーム(ARN)に置き換える。

```
{
"Version": "2012-10-17",
"Statement": [
    {
        "Sid": "VisualEditor0",
        "Effect": "Allow",
        "Action": [
            "dynamodb:PutItem",
            "dynamodb:DeleteItem",
            "dynamodb:GetItem",
            "dynamodb:Scan",
            "dynamodb:Query",
            "dynamodb:UpdateItem"
        ],
        "Resource": "YOUR-TABLE-ARN"
    }
    ]
}
```

入力できたら「ポリシーの確認」を選択する。
![スクリーンショット 2020-08-14 18.23.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/060e2535-62ac-a1cc-f167-20c7e639b8f8.png)
名前　「HelloWorldDynamoPolicy」と入力し、「ポリシーの作成」を選択。
![スクリーンショット 2020-08-14 18.24.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/dab4f476-7b7e-20a5-47ac-806979375c95.png)
HelloWorldFunctionの関数コードで下記内容を入力する。
入力できたら右上の「保存」ボタンを選択。

```
# import the json utility package since we will be working with a JSON object
import json
# import the AWS SDK (for Python the package name is boto3)
import boto3
# import two packages to help us with dates and date formatting
from time import gmtime, strftime

# create a DynamoDB object using the AWS SDK
dynamodb = boto3.resource('dynamodb')
# use the DynamoDB object to select our table
table = dynamodb.Table('HelloWorldDatabase')
# store the current time in a human readable format in a variable
now = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())

# define the handler function that the Lambda service will use as an entry point
def lambda_handler(event, context):
# extract values from the event object we got from the Lambda service and store in a variable
    name = event['firstName'] +' '+ event['lastName']
# write name and time to the DynamoDB table using the object we instantiated and save response in a variable
    response = table.put_item(
        Item={
            'ID': name,
            'LatestGreetingTime':now
            })
# return a properly formatted JSON object
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda, ' + name)
    }
```

![スクリーンショット 2020-08-14 18.25.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/1b3847af-a722-a5d8-6468-b059de2f4e24.png)

テストボタンを選択。
実行結果: 成功が表示される。
![スクリーンショット 2020-08-14 18.26.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/cdaf45ef-188e-07ae-ade2-2e4ca790e0e6.png)
DynamoDB>テーブルを選択。テーブルの`HelloWorldDatabase`を選択する。
![スクリーンショット 2020-08-14 18.26.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/d4a3e8c9-5816-3d66-d569-11ce2e74d215.png)
`Ada Lovelace`が表示されている
![スクリーンショット 2020-08-14 18.27.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/1737ecc6-c510-b2b7-d094-c19c23c8991b.png)

## REST APIを呼び出す

index.htmlファイルを下記のように書き換える。その際、YOUR-API-INVOKE-URLをAPI呼び出しURLに書き換える。そしてファイルを保存する。 

```
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Hello World</title>
    <!-- Add some CSS to change client UI -->
    <style>
    body {
        background-color: #232F3E;
        }
    label, button {
        color: #FF9900;
        font-family: Arial, Helvetica, sans-serif;
        font-size: 20px;
        margin-left: 40px;
        }
     input {
        color: #232F3E;
        font-family: Arial, Helvetica, sans-serif;
        font-size: 20px;
        margin-left: 20px;
        }
    </style>
    <script>
        // define the callAPI function that takes a first name and last name as parameters
        var callAPI = (firstName,lastName)=>{
            // instantiate a headers object
            var myHeaders = new Headers();
            // add content type header to object
            myHeaders.append("Content-Type", "application/json");
            // using built in JSON utility package turn object to string and store in a variable
            var raw = JSON.stringify({"firstName":firstName,"lastName":lastName});
            // create a JSON object with parameters for API call and store in a variable
            var requestOptions = {
                method: 'POST',
                headers: myHeaders,
                body: raw,
                redirect: 'follow'
            };
            // make API call with parameters and use promises to get response
            fetch("YOUR-API-INVOKE-URL", requestOptions)
            .then(response => response.text())
            .then(result => alert(JSON.parse(result).body))
            .catch(error => console.log('error', error));
        }
    </script>
</head>
<body>
    <form>
        <label>First Name :</label>
        <input type="text" id="fName">
        <label>Last Name :</label>
        <input type="text" id="lName">
        <!-- set button onClick method to call function we defined passing input values as parameters -->
        <button type="button" onclick="callAPI(document.getElementById('fName').value,document.getElementById('lName').value)">Call API</button>
    </form>
</body>
</html>
```

「アップロード」ボタンを選択。
![スクリーンショット 2020-08-14 19.20.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/2788bad2-7d54-8363-be83-4b67f9d23b6d.png)
`index.html`ファイルを追加して、「次へ」ボタンを選択。
パブリックアクセス許可の管理で「このオブジェクトへのパブリック読み取りアクセスを付与する」を選択する。左下のアップロードボタンを選択する。
![スクリーンショット 2020-08-14 19.22.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/4a24ade8-43a9-35c7-9bb6-6a0dd8234b52.png)
`index.html`>「オブジェクト URL」を選択する。
![スクリーンショット 2020-08-14 9.05.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/85887cf5-e441-ddb7-7d19-0e2d17f01a1a.png)
ウェブアプリがブラウザに読み込まれる。
名前 (または任意の名前) を入力し、「API を呼び出す」ボタンをクリック。
「Hello from Lambda, First Name Last Name」が表示される。
![スクリーンショット 2020-08-14 19.25.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/b3395c11-f2f3-8431-b24a-0d7ab7730641.png)

##まとめ
サーバーレスでウェブアプリを構築する技術を学んだ。これらを応用し、今後ウェブアプリ等を作成したいと思う。