【AWS】基本的なWebアプリケーションをクイック開発する


下記公式サイトのチュートリアルを参考に基本的なWebアプリケーションを一時間で構築してみる。

https://aws.amazon.com/jp/getting-started/hands-on/build-web-app-s3-lambda-api-gateway-dynamodb/

〇 今回構築したWebアプリケーションの構成図
![We_applivcation_basic.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/cbc9c613-991d-66a3-c5b9-84c94b19e96f.jpeg)

## 開発手順

### 1. AWS AmplifyでWebアプリケーションの静的リソースをデプロイする

1. ローカルPCに下記のindex.htmlを用意し、ZIP形式で圧縮する
    ```html:index.html
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Hello World</title>
    </head>
    
    <body>
        Hello World
    </body>
    </html>
    ```
1. [Amplify コンソール](https://us-west-2.console.aws.amazon.com/amplify/home)にログインし、「使用を開始する」を押す
    ※もしくは新しいアプリケーションを押す
1. Amplify ホスティングの使用を開始するを押す
    ※もしくはウェブアプリケーションをホストを押す
1. [Git プロバイダーなしでデプロイ] を選択して、続行を押す
    ![Amplify1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/17fdc646-6abd-d2f5-61ad-a57932fd366a.png)
1. アプリケーションの名前にGettingStarted、環境名にdevを入力する
1. ドラッグ & ドロップを選択する
1. 先ほどのindex.html.zipをドロップして、保存してデプロイを押す
1. GettingStartedのコンソール画面中部のDomainのURLをクリックする
1. Hello Worldが表示されたら、正常にHTTPS通信でのwebページが作成された

### 2. AWS Lambdaでサーバーレス関数を構築する
1. [AWS Lambda コンソール](https://ap-northeast-1.console.aws.amazon.com/lambda/home?region=ap-northeast-1#)にログインする
1. 関数の作成を押す
1. 関数名をHelloWorldFunction、ランタイムにPython 3.9を選択する
1. 関数の作成を押す
1. 関数のコードを以下に置き換える
    ```Python
    # 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)
        }
    ```
1. Deployを押す
1. Testを押す
1. イベント名にHelloWorldTestEventを入れる
1. イベントJSONを以下に置き換える
    ```JSON
    {
    "firstName": "Ada",
    "lastName": "Lovelace"
    }
     ```
1. テストタブへ行き、右上のテストを押す
1. 実行結果：成功が出て、"\"Hello from Lambda, Ada Lovelace\""が出れば成功

### 3. API Gatewayでサーバーレス関数をリンクする
1. [API Gateway Console](https://console.aws.amazon.com/apigateway/main/) にログインする
1. REST APIで構築を押す
1. プロトコルの選択でREST、新しいAPIの作成で新しいAPIを選択する
1. API名をHelloWorldAPIを入れ、エンドポイントタイプにエッジ最適化を選択する
   ![APIgateway2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/6d7c6098-c2f3-5fe6-6a38-db1d185d7d3a.png)
1. API の作成を押す
1. アクション->メソッドの作成->POST->横のチェックを押す
    ![apigateway3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/0db22868-96e4-c7a4-6b98-c9759af67eb8.png)
1. 統合タイプにLambda 関数、Lambda 関数にarn:aws:lambda:us-east-1:*****:function:HelloWorldFunctionを入れて、保存を押す
1. アクション->CORS を有効にするを押す
    ![APIgateway4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/a30a468f-45d5-5c12-21b7-b3825c6d1b7d.png)
1. POSTチェックボックスを選択して、CORS を有効にして既存の CORS ヘッダーを置換を押す
1. アクション->APIをデプロイを押す
1. デプロイされるステージで新しいステージを押し、ステージ名にdevを入れ、デプロイを押す
    ![APigateway5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/161b0fd3-294d-5353-d33e-ab65c39b531d.png)
1. URL の呼び出しのURLを控えておく
    ![APIgateway6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/a75fa71d-988b-1a2a-c296-23a48abef543.png)
1. リソース->POST->小さな青い稲妻のテストを押す
1. 下記のJSONを入力し、テストを押す
    ```JSON
    {
        "firstName":"Grace",
        "lastName":"Hopper"
    }
    ```
1. bodyに "\"Hello from Lambda, Grace Hopper\"が返ってきていれば成功

### 4. AWS DynamoDB テーブルを作成し、Lambda 関数がデータを格納できるようにする
1. [Amazon DynamoDB コンソール](https://console.aws.amazon.com/dynamodb/home)にログインする
1. 「テーブルの作成」を押す
1. テーブル名にHelloWorldDatabase、パーティションキーにIDと入力する
    ![DynamoDB2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/48c1ce52-74f1-3a27-86a5-593190f6eabf.png)
1. テーブルの作成を押す
1. HelloWorldDatabaseの設定画面で、追加情報を押し、Amazon リソースネーム (ARN)をコピーしておく
    ![DynamoDB3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/6e86ef9b-152f-9be4-a6ea-fb8629caa1f8.png)
1. [AWS Lambda コンソール](https://console.aws.amazon.com/lambda/home)を開き、HelloWorldFunctionを選択する
1. 設定タブ->アクセス権限->実行ロールのURLを押す
    ![Lambda2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/58a1c60e-2ee2-e80e-0dc8-383d00dbc080.png)
1. IAMのロール画面に遷移する
1. 許可タブの許可を追加で、インラインポリシーの作成を押す
    ![IAM2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/5430234a-0a2b-c948-8f86-ac335cb58b39.png)
1. JSONタブをクリックし、以下を入力する。
    ※YOUR-TABLE-ARNは、DynamoDBのリソースフィールドのテーブルの ARN を置き換える
    ```JSON
    {
    "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"
        }
        ]
    }
    ```
1. 名前にHelloWorldDynamoPolicyを入れ、ポリシーの作成を押す
1. Lambda関数のコンソール画面に戻り、下記のコードに置き換え、デプロイを押す
    ```Python
    # 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)
        }
    ```
1. テストタブ->テストを押す
1. DynamoDBのHelloWorldDatabaseのテーブル->テーブルアイテムの探索で下部にAda Lovelaceを押す
1. IDと実行時間が入力されていれば成功

![DynamoDB1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/1443747f-c74d-a50a-c3a3-20dd9532ab27.png)

### 5. HTMLを編集して、Webアプリケーションに対話性を追加する
1. index.html ファイルを開いて、下記に置き換える。
    ※行 41 "YOUR-API-INVOKE-URL"に API 呼び出しAPIGatewayのステージのURLを追加する
    ```html:index.html
    <!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>
    ```
1. Zip化して、[Amplify コンソール](https://us-west-2.console.aws.amazon.com/amplify/home)を開いて、新しいindex.htmlに置き換える
1. DomainのURLを開いてみる

![画面html1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/dd48ccc2-5264-0298-8a82-2a282dbf8ee7.png)

1. First NameとLast Nameを入力して、Call APIでLambdaを呼び出す
1. するとDynamoDBのテーブル->テーブルアイテムの探索で、下部に呼び出した人の名前と時間が入力されていく

## まとめ
今回は、AWSサービスを用いて、基本的なWebアプリケーションをたった一時間程度で構築した。AWS Amplifyを使うことで、時間のかかるフロントエンドのプラットフォーム開発が一瞬で終わり、ユーザーは純粋にWebアプリケーションの中身に時間を使うことができる。