API GatewayとLambdaで簡易Webアプリを作ってみる

# はじめに
本記事は、AWSの練習を兼ねて簡易なWebアプリケーションを作成した備忘録です。フォームから送信したテキストをLambdaで処理するようなシステムを作ります。

複雑なテキスト処理の実装はしませんが、今後の記事で感情分析などの機械学習を用いた処理を追加しようと思います。
# 作りたいもの
ブラウザ上で入力したテキストを処理して、処理結果を返すAPIを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/3a85a8bf-289b-8dd2-aba1-d2a5e190c3e7.png)
上図のように、フォームにテキストを入力して送信すると、Lambda関数で定義した処理が実行され、処理結果が表示されるような仕組みを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/91c0df24-e6f8-9663-a5e6-43cecd49c557.png)




ネットワーク図は以下の通りです。API GatewayとLambdaを使用します。
なお、ネットワーク図の作成には[こちらの記事](https://aws.taf-jp.com/blog/61489)を参考にしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/ec649a6d-f914-ffb7-a48e-9c0c9b17ff4b.png)



# 前提知識
今回使用するAWSの機能について簡単に述べます。
## Amazon API Gateway
API Gatewayは、APIの作成・管理が可能なフルマネージドサービスです。HTTP API または REST API を使用して RESTful API を作成することができますが、APIの種類によって実現できる機能は異なります。RESTとREST APIについては、[こちらの記事](https://tech.012grp.co.jp/entry/rest_api_basics)がわかりやすいです。

また、API Gatewayに関する詳細な情報は、[公式サイト](https://aws.amazon.com/jp/api-gateway/)や[こちらの解説記事](https://dev.classmethod.jp/articles/what-does-amazon-api-gateway-do/)がわかりやすいです。
API Gatewayは、クライアントからリクエストを受け取ってバックエンドに渡し、バックエンドからのレスポンスをクライアントに返します。なお、本記事のバックエンドはLambdaになります。


## AWS Lambda
AWS Lambdaは、サーバーレスのコンピューティングサービスです。ユーザ自身でサーバーのプロビジョニングや管理をする必要なく、事前に作成しておいたプログラムをイベント駆動で実行することができます。イベントの例を挙げると、Amazon API Gateway 経由のHTTP リクエストやAmazon S3バケット内のオブジェクト変更などです。また、実行するプログラムは、Go、Java、Python（以下略）等の複数の言語で記述することができます。

AWSLambdaに関する詳細な情報は、[公式サイト](https://aws.amazon.com/jp/lambda/)や[こちらの解説記事](https://codezine.jp/article/detail/8446)がわかりやすいです。


# 作ってみる
## フロント
フロントの実装ですが、以下の記事を参考にして作成しました。
[HTMLファイルからPOSTでJSONデータを送信する](https://qiita.com/kidatti/items/21cc5c5154dbbb1aa27f)

なお、実際に使用したコードは以下の通りです。
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>HTMLファイルからPOSTでJSONデータを送信する</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>

<script type="text/javascript">
    $(function(){
        $("#response").html("Response Values");

        $("#button").click( function(){
            // ここのURLはAPIのデプロイ時に表示されるURL（後述）に差し替える
            var url = "https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev";
                var JSONdata = {
                    "key1": $("#text").val()
                };

            alert(JSON.stringify(JSONdata));

            $.ajax({
                type : 'post',
                url : url,
                data : JSON.stringify(JSONdata),
                contentType: 'application/json',
                dataType : 'json',
                scriptCharset: 'utf-8',
                success : function(data) {

                    // Success
                    alert("success");
                    alert(JSON.stringify(data));
                    $("#response").html(JSON.stringify(data));
                },
                error : function(data) {

                    // Error
                    alert("error");
                    alert(JSON.stringify(data));
                    $("#response").html(JSON.stringify(data));
                }
            });
        })
    })
</script>

</head>
<body>
    <h1>HTMLファイルからPOSTでJSONデータを送信する</h1>
    <p>入力テキスト: <input type="text" id="text" size="30" placeholder="テキストを入力してください"></p>
    <p><button id="button" type="button">送信</button></p>
    <textarea id="response" cols=120 rows=10 disabled></textarea>
</body>
</html>

```

## Lambda関数の作成
コンソールにサインインしてLamnbdaのページを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/2cfe8eb7-efd1-32b8-2819-389c2b484619.png)
そして、「関数の作成」ボタンをクリックします。

今回は既に用意されているサンプルコードを使用することにします。設計図の使用を選択して、設計図名は「Hello world function」のPythonを選択します。また、実行ロールはデフォルトの設定とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/0331ccd9-f66d-5bd9-0f17-369d560e496a.png)

そして、ページ下部にある「関数の作成」ボタンを押します。

関数の作成に成功すると以下の画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/f750fadd-15a1-920e-ec08-89cf86e424f6.png)

ページ下部のコードタブのコード欄には、実行したいコードを書くことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/462a26d4-8dfb-98af-4f79-8fef016b7375.png)

テストタブからLambda関数の処理をテストすることができます。イベントJSONはlambda_hadler関数の引数のeventに対応しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/38cbc2d2-ed4c-f63e-8cac-86aa01f76c8c.png)

実行に成功すると、所要時間や課金期間、ログとともに実行結果を確認することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/4b951d1d-b1b6-de90-5e4e-9594105fb1e6.png)

また、初回の関数実行には時間がかかります（コールドスタート）。これはLambdaの関数インスタンスの生成に時間がかかっているためです。二回目以降の実行では初回と比べて所要時間は短くなります。実行されていないインスタンスは一定時間後に消失してしまうため、サービス開発等でLambdaを利用する際には注意が必要です。



## APIの作成
次に、API Gatewayのページを開きます。「APIを作成」のボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/67e452a2-ab66-949c-64b7-8eda848f0feb.png)

すると、APIのタイプを選択する画面に移ります。
APIタイプが複数ありますが、ここでは上から三つ目のREST APIを構築します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/a58fc333-7508-faa1-496f-73d0a6d154ad.png)

設定はデフォルトのままでAPIの作成を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/44348a2c-61a1-254b-4575-96e2fcbfd361.png)

アクションタブからメソッドの作成を選び、POSTを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/91d8d403-38d5-b28c-e852-4ad5619f88bd.png)

するとセットアップ画面が表示されます。統合タイプにLambda関数を選び、先ほど作成したLambda関数を選択します。保存ボタンを押すと確認画面が表示されるので、OKを押して保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/7daeac8a-f1cf-fe05-9729-42016bf977f6.png)

次に、アクションタブからCORS（Cross-Origin Resource Sharing）の有効化を行います。設定はデフォルトのままで保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/b5a644f3-30a4-951c-0582-adfb14f1cb81.png)

CORSに関する話は[こちらの記事](https://dev.classmethod.jp/articles/amazon-api-gateway-cors/)がわかりやすいです。CORSを有効化することにより、先ほど作成したWebページからAPI Gatewayで作成したAPI（異なるオリジンにある）を動的に呼び出すことができるようになります。

次に、左側タブのゲートウェイのレスポンスを選択し、DEFAULT 4XXを選び、編集ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/fd770a4d-2fba-5fec-fba5-22406e3a04ad.png)

レスポンスヘッダーに以下を追加します。
```
  Access-Control-Allow-Origin: '*'
```

[IT用語辞典](https://e-words.jp/w/Access-Control-Allow-Origin.html)によると、
>Access-Control-Allow-Originはサーバからクライアントへの応答であるHTTPレスポンスで用いられるヘッダ項目の一つで、複数のサーバにまたがってコンテンツを組み合わせる「CORS」（Cross-Origin Resource Sharing：クロスオリジンリソース共有）を許可するかどうかを通知するのに用いられる。

と説明されています。

Access-Control-Allow-Origin: *と設定すると、すべてのオリジンからのリクエストを許可します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/ee007a63-830f-f15e-9797-cf2eeb54b812.png)

保存を押して設定は完了です。

最後にアクションタブからAPIのデプロイを行います。ここでは、[新しいステージ]を選択しステージ名を決めてデプロイします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/c7b77cc1-6919-f93b-29b9-d73f408fe2dc.png)

デプロイが完了すると、画面上部にURLが表示されます。このURLで```var url = "https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev";```の""内を書き換えます。

# 実行結果
無事APIを作成することができたので、実際にWebページからAPIを叩いてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2405498/07b0b17b-a40b-7ea5-a79f-a4a22ac4cbb1.png)

正常に動いていることが確認できました。
# まとめ
POSTでjsonデータを送信し、API Gateway経由のリクエストがあったときに作成したLambda関数を呼び出し、実行結果をブラウザ上で確認するというシンプルなシステムを作成しました。

最小限の機能しか備えていませんが、ECRに独自のコンテナイメージをプッシュしてLambdaと連携させる等工夫のしどころはいろいろあると思うので、今後の記事でそのあたりにも触れていこうと思います。

# 参考文献
- [AWSサーバーレス連絡フォームの作り方💪(AWS Lambda、API Gateway、SES)](https://qiita.com/ThanhLe/items/d491b535270fa2edd2f7)
- [AWS構成図をパパッと作ろう！─ diagrams.net (draw.io) をさわってみた](https://aws.taf-jp.com/blog/61489)
- [REST APIとは？ざっくりと理解してみる【初心者向け】](https://tech.012grp.co.jp/entry/rest_api_basics)
- [Amazon API Gateway](https://aws.amazon.com/jp/api-gateway/)
- [Amazon API Gateway は何をしてるのか](https://dev.classmethod.jp/articles/what-does-amazon-api-gateway-do/)

- [AWS Lambda](https://aws.amazon.com/jp/lambda/)
- [初めてのAWS Lambda～AWS Lambdaで始めるイベントドリブンアプリケーション](https://codezine.jp/article/detail/8446)

- [HTMLファイルからPOSTでJSONデータを送信する](https://qiita.com/kidatti/items/21cc5c5154dbbb1aa27f)
- [Amazon API Gateway をクロスオリジンで呼び出す (CORS)](https://dev.classmethod.jp/articles/amazon-api-gateway-cors/)
- [IT用語辞典: Access-Control-Allow-Origin 【ACAO】](https://e-words.jp/w/Access-Control-Allow-Origin.html)