【AWS】はじめてのサーバーレスAPI構築

この記事は[マイナビ Advent Calendar 2021](https://qiita.com/advent-calendar/2021/mynavi)の21日目の記事となります。

#目的
AWS Lambda、API Gateway、Amazon RDSを利用して簡易的なサーバーレスAPI（以下、簡易API）を構築します。簡易APIにリクエストを送ると、RDSから値を取得しレスポンスを返すという仕様になります。

###構成図
![Untitled Diagram (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304307/c980561e-5ab7-01c4-14ad-eb1cce3b854d.png)

#前提・注意事項

- 本ソースコードを試す場合は、APIとして外部公開される危険性を考慮し、DBに機密なデータが無いことを確認の上お試しください。
- 事前にRDSを構築しておく必要があります。今回の記事では詳細は省略しております。
- 認証に関しては、今回説明しておりませんが、IAM認証やCognito認証、API Gateway Lambda オーソライザー等を利用して設定してください。

#手順①Lambda⇔DB連携

###1. Lambda関数作成
1) [Lambdaコンソール画面](https://ap-northeast-1.console.aws.amazon.com/lambda/home)にアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304307/ff62b998-c164-12d9-9597-960acc569aa9.png)
2)「関数の作成」ボタン押下
3) 関数設定

```
オプション：一から作成
関数名：各自命名
ランタイム：Python3.9
アーキテクチャ：arm64
アクセス権限の実行ロール：基本的なLambdaアクセス権限で新しいロールを作成
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304307/a25bea61-a9cc-839e-c915-4f173240eb5e.png)
4) 関数詳細設定
詳細設定を開き「ネットワークを有効化」にチェックを入れ、下記の通り設定する

```
VPC：DBと同じ
サブネット：DBと同じ
セキュリティグループ：DBと同じ
```

5)「関数の作成」ボタン押下
ボタン押下後、関数が作成されるまで2~3分ほど時間がかかるため、しばらく待機。
6) app.zipアップロード
下記コマンドを実行し、ローカルでapp.zipファイルを作成後、コードソース内から「アップロード元」→「.zipファイル」→「アップロード」→「app.zip」→「保存」でapp.zipをアップロードする

```
# pymsqをlローカルにインストール
pip3 install pymysql -t .
# zip化
zip -r app.zip /path/to/workdir/*
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304307/0fea727b-ebe4-ebef-5db7-4e25e27d4c76.png)
7) ディレクトリ構成変更
関数のディレクトリ構成を下記のように変更する

```
関数名
　└pymsql
　└PyMySQL-1.0.2.dist-info
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304307/74b5a165-bec0-ad80-f601-9f8063df4ae2.png)
8) lambda_function.pyファイル作成

```python:lambda_function.py
import sys
import json
import pymysql

# DB接続情報設定
host = '<DBホスト名>'
user = '<DBユーザー名>'
passwd = '<DBパスワード>'
db_name = '<DB名>'

def lambda_handler(event, context):
    try:
        # DB接続
        conn = pymysql.connect(host = host, user = user, passwd = passwd, db = db_name, connect_timeout = 5)
    except Exception as e:
        print("error")
        print(e)
        
    # カーソル作成
    cursor = conn.cursor()
    # クエリ
    query = "SELECT id, name FROM test_t WHERE id = %s"
    # クエリ実行
    cursor.execute(query, (event['id']))
    # クエリ実行結果取得
    result = cursor.fetchall()
    return {
        'statusCode': 200,
        'body': result
    }
```

9) 「Deploy」ボタン押下

###2. Lambda関数テスト
1) テストイベント作成
下記の通りテストイベント作成し「テスト」ボタン押下

```
例）
テンプレート：id
名前：test_q

{
  "id": 1
}
```

2) 実行結果確認
実行結果を開き、DBから値取得できていることを確認

#手順②API Gateway⇔Lambda連携
###1. 関数のトリガーにAPI Gatewayを設定
1) Lambda関数の概要から「トリガーを追加」ボタン押下
2) API Gatewayを選択→APIの選択肢から「APIを作成する」を選択
3) API情報設定
下記の通りAPI情報を設定し「追加」ボタン押下

```
・APIタイプ：REST API
・セキュリティ：オープン
▼追加の設定
・API名：自身で分かりやすい名前を指定
・デプロイされるステージ：develop
```

###2. API Gatewayメソッド作成

1) [API Gatewayコンソール画面](https://console.aws.amazon.com/apigateway)にアクセス
2) 上記作成したAPI Gateway選択
3) ANYメソッド削除
リソースのANYメソッドを選択したまま「アクション」ボタンを押下し「メソッドの削除」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304307/36ab9451-6178-9aeb-00d8-1bfd5013b0c3.png)
4) 「削除」ボタン押下
アラートで「ANY を削除してよろしいですか?」と表示されたら「削除」ボタン押下
5) 「GET」メソッド作成
[リソース]にて「アクション」ボタン押下→「メソッドの作成」選択→プルダウンから「GET」選択→☑マーク押下
6) 設定
セットアップ画面にて、下記の通り設定し「保存」ボタン押下

```
・統合タイプ： Lambda 関数
・Lambda プロキシ統合の使用：チェックなし
・Lambda リージョン：ap-northeast-1
・Lambda 関数：既に作成したLambda関数名
・デフォルトタイムアウトの使用：☑あり

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304307/564c942a-f48d-c31f-6c77-493fcabbb06e.png)
7) 権限追加
アラートで「Lambda 関数に権限を追加する」と表示されたら「OK」ボタン押下

###3. API Gatewayメソッドリクエスト編集
1) [リソース]の[GET - メソッドの実行]から[メソッドリクエスト]選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304307/b8778bff-000b-e7b6-c7da-778c85d7c65e.png)
2) [設定]の[認可]のプルダウンから[AWS IAM]を選択
3) [設定]の[リクエストの検証]のプルダウンから「クエリ文字列パラメータおよびヘッダーの検証」を選択し☑マーク入れる
4) [URL クエリ文字列パラメータ]から「クエリ文字列の追加」選択→「名前：id」を入力→☑マーク入れる

###4. API Gateway統合リクエスト編集
1) [リソース]の[GET - メソッドの実行]から[統合リクエスト]選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304307/826ba875-8d3a-4ee6-e97a-3638efe76210.png)
2) 「マッピングテンプレートの追加」→Content-Typeにapplication/jsonを入力→☑マーク押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304307/69824258-5dc9-1e2c-15fc-a0d97cdb2950.png)
3) アラートで「パススルー動作の変更」と表示されたら「はい、この統合を保護します」ボタン押下
4) テンプレートのテキストエディタに下記を入力し、「保存」ボタン押下

```
{
    "id" : "$input.params('id')"
}
```

###5. APIデプロイ
1) [リソース]の[アクション]から「APIのデプロイ」押下
アラートで「APIのデプロイ」と表示されたら下記を入力し「デプロイ」ボタン押下

```
・デプロイされるステージ：develop
・デプロイメントの説明：テスト用Lambda APIのデプロイ
```

###6. APIテスト
1) [リソース]の[GET]メソッドを選択し[テスト]押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304307/0342358a-d57a-8c85-b840-52180244eca6.png)
2) クエリ文字列に「id=1」を入力し「テスト」ボタンを押下
3) レスポンス結果確認
DBから取得した値がレスポンスとして返ってくれば成功