あめがえるのITブログ

頑張りすぎない。ほどほどに頑張るブログ。

API Gateway(HTTP API)+Lambda(Python)経由でDynamoDBを操作してみた

Web API(API Gateway(HTTP API)+Lambda)の仕組みでDynamoDBを操作する仕組みを作ってみた。

やること

適当なWindows端末からcurlコマンドを打ち、API Gateway(HTTP API)からバックエンドのLambda(Python)、DynamoDBに接続し操作する。
curlコマンドは事前にインストールしておくこと

構成

実践!

1.IAMポリシー作成
 1-1.[IAM]-[ポリシー]-[ポリシー作成]
 1-2.JSONに下記を入力

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1428341300017",
      "Action": [
        "dynamodb:DeleteItem",
        "dynamodb:GetItem",
        "dynamodb:PutItem",
        "dynamodb:Query",
        "dynamodb:Scan",
        "dynamodb:UpdateItem"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Sid": "",
      "Resource": "*",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Effect": "Allow"
    }
  ]
}

 1-3.確認して作成画面が表示されたら下記を入力
  ・ポリシー名:lambda-dynamo-policy
  ・説明:空白
 1-4.[ポリシーの作成]をクリック

2.ロール作成
 2-1.[IAM]-[ロール]-[ロールを作成]
 2-2.信頼されたエンティティを選択が表示されたら下記を入力
  ・信頼されたエンティティタイプ:AWSのサービス
  ・ユースケース:Lambda
 2-3.[次へ]をクリック
 2-4.許可を追加が表示されたら[lambda-dynamo-policy]を選択
 2-5.[次へ]をクリック
 2-6.ロール名は[lambda-dynamo-role]を入力し、[ロールを作成]をクリック

3.DynamoDBテーブル作成
 3-1.[DynamoDB]-[テーブル]-[テーブルの作成]
 3-2.テーブルの作成画面が表示されたら下記を入力
  ・テーブル名:sample-dynamodb
  ・パーティションキー:id
 3-3.[テーブルの作成]をクリック

4.DynamoDB項目作成
 4-1.[DynamoDB]-[テーブル]-[sample-dynamodb]
 4-2.[アクション]-[項目を作成]
 4-3.下記を入力
  ・id:123
  ・value:test ※[新しい属性の追加]-[文字列]で追加して入力する

5.Lambda関数作成
 5-1.[Lambda]-[関数]-[関数の作成]
 5-2.関数の作成画面が表示されたら下記を入力
  ・一から作成:✅
  ・関数名:sample-function
  ・ランタイム:Python3.10
  ・実行ロール:既存のロールを使用する
  ・既存のロール:lambda-dynamo-role
 5-3.[関数の作成]をクリック
 5-4.コードに下記を入力
  ※POSTやPUT、DELETEもあるが今回はGETのみ使用

import json
import boto3

dynamodb = boto3.client('dynamodb')
table_name = 'sample-dynamodb'

def lambda_handler(event, context):
    method = event["routeKey"]
    if "PUT" in method:
        return create(event, context)
    elif "GET" in method:
        return read(event, context)
    elif "DELETE" in method:
        return delete(event, context)
    else:
        return {
            "statusCode": 400,
            "body": json.dumps({"error": "Unsupported method"})
        }
        
def create(event, context):
    id = event["pathParameters"]["id"]
    value = event["pathParameters"]["value"]
    options = {
        'TableName': table_name,
        'Item': {
            'id': {'S': id},
            'value': {'S': value},
        }
    }
    response = dynamodb.put_item(**options)
    
    res = response["ResponseMetadata"]["HTTPStatusCode"]
    if res == 200:
        return {
            "statusCode": 200,
            "body": json.dumps(response)
        }
    else:
        return {
            "statusCode": 404,
            "body": json.dumps({"error": "Item not found"})
        }

def read(event, context):
    id = event["pathParameters"]["id"]
    options = {
        'TableName': table_name,
        'Key': {
            'id': {'S': id},
        }
    }
    response = dynamodb.get_item(**options)
    
    if 'Item' in response:
        return {
            "statusCode": 200,
            "body": json.dumps(response)
        }
    else:
        return {
            "statusCode": 404,
            "body": json.dumps({"error": "Item not found"})
        }
        
def delete(event, context):
    id = event["pathParameters"]["id"]
    options = {
        'TableName': table_name,
        'Key': {
            'id': {'S': id},
        }
    }
    response = dynamodb.delete_item(**options)
    
    res = response["ResponseMetadata"]["HTTPStatusCode"]
    if res == 200:
        return {
            "statusCode": 200,
            "body": json.dumps(response)
        }
    else:
        return {
            "statusCode": 404,
            "body": json.dumps({"error": "Item not found"})
        }


6.Lambdaテスト
 6-1.作成したLambda関数を選択し、[Test]をクリック
 6-2.テストイベントを設定が表示されたら下記を入力
  ・新しいイベントを作成:✅
  ・イベント名:MyEvent
  ・イベントJSON:下記を入力

{
    "routeKey": "GET /items/123/",
    "pathParameters": {
        "id": "123",
        "value": "test1"
    }
}

 6-3.[Test]をクリック
 6-4.下記のような返答が返ってくることを確認

Test Event Name
GET

Response
{
  "statusCode": 200,
  "body": "{\"Item\": {\"id\": {\"S\": \"123\"}, \"value\": {\"S\": \"test\"}}, \"ResponseMetadata\": {\"RequestId\": \"0IPTDQ3K2C66RCCFCM75DKM5RRVV4KQNSO5AEMVJF66Q9ASUAAJG\", \"HTTPStatusCode\": 200, \"HTTPHeaders\": {\"server\": \"Server\", \"date\": \"Mon, 08 May 2023 10:44:17 GMT\", \"content-type\": \"application/x-amz-json-1.0\", \"content-length\": \"48\", \"connection\": \"keep-alive\", \"x-amzn-requestid\": \"0IPTDQ3K2C66RCCFCM75DKM5RRVV4KQNSO5AEMVJF66Q9ASUAAJG\", \"x-amz-crc32\": \"2920716868\"}, \"RetryAttempts\": 0}}"
}


7.API Gateway作成
 7-1.[API Gateway]-[HTTP API]内の[構築]をクリック
 7-2.APIの作成が表示されたら下記を入力
  ・API名:SampleAPI
 7-3.[次へ]をクリック
 7-4.ルートを設定が表示されたら、なにもせず[次へ]
 7-5.ステージを定義が表示されたら[ステージを追加]を選択し、ステージ名に[staging]を入力
 7-6.[次へ]をクリック
 7-7.確認して作成が表示されたら[作成]をクリック
 7-8.左ペインから[Routes]を選択
 7-9.[Create]をクリック
 7-10.下記のようなツリー構造を作成
  /items(ルート)
   └ GET(メソッド)
   /{id}(ルート)
    └ GET(メソッド)
    └ DELETE(メソッド)
    /{value}(ルート)
     └ PUT(メソッド)
 7-11.作成したメソッドをクリックし、右にルートの詳細が表示されたら[統合をアタッチする]をクリック
 7-12.プルダウンで[sample-function]を選択し、[統合をアタッチする]をクリック
 7-13.[デプロイ]をクリック
 7-14.デプロイを作成してステージへアタッチが表示されたら、下記を入力
  ・ステージを選択:staging
  ・この出プロの変更について説明:空白
 7-15.[ステージへデプロイ]

8.API Gateway動作確認
 8-1.下記curlコマンドを実行する
  ・GET

>curl -X GET https://wobmkjeu87.execute-api.ap-northeast-1.amazonaws.com/staging/items/123
{"Item": {"id": {"S": "123"}, "value": {"S": "test"}}, "ResponseMetadata": {"RequestId": "1Q62TFT1T9QU5N6L7BKK4PQVT3VV4KQNSO5AEMVJF66Q9ASUAAJG", "HTTPStatusCode": 200, "HTTPHeaders": {"server": "Server", "date": "Mon, 08 May 2023 10:35:59 GMT", "content-type": "application/x-amz-json-1.0", "content-length": "48", "connection": "keep-alive", "x-amzn-requestid": "1Q62TFT1T9QU5N6L7BKK4PQVT3VV4KQNSO5AEMVJF66Q9ASUAAJG", "x-amz-crc32": "2920716868"}, "RetryAttempts": 0}}

  ・CREATE

>curl -X PUT https://wobmkjeu87.execute-api.ap-northeast-1.amazonaws.com/staging/items/1234/test1
{"ResponseMetadata": {"RequestId": "T04KFSJI67NP0TDTA6NH94C0TBVV4KQNSO5AEMVJF66Q9ASUAAJG", "HTTPStatusCode": 200, "HTTPHeaders": {"server": "Server", "date": "Mon, 08 May 2023 10:36:11 GMT", "content-type": "application/x-amz-json-1.0", "content-length": "2", "connection": "keep-alive", "x-amzn-requestid": "T04KFSJI67NP0TDTA6NH94C0TBVV4KQNSO5AEMVJF66Q9ASUAAJG", "x-amz-crc32": "2745614147"}, "RetryAttempts": 0}}

  ・DELETE

>curl -X DELETE https://wobmkjeu87.execute-api.ap-northeast-1.amazonaws.com/staging/items/1234/test1
{"ResponseMetadata": {"RequestId": "MI42GUC3BSDJER8B4O9JEN5S7JVV4KQNSO5AEMVJF66Q9ASUAAJG", "HTTPStatusCode": 200, "HTTPHeaders": {"server": "Server", "date": "Mon, 08 May 2023 10:37:27 GMT", "content-type": "application/x-amz-json-1.0", "content-length": "2", "connection": "keep-alive", "x-amzn-requestid": "MI42GUC3BSDJER8B4O9JEN5S7JVV4KQNSO5AEMVJF66Q9ASUAAJG", "x-amz-crc32": "2745614147"}, "RetryAttempts": 0}}




感想

API GatewayのHTTP APIを使用すればブラウザのURLからPOSTなどのGET以外のメソッドを送信できると思っていたがREST APIと同じでダメでした。
やっぱりWeb APIってのはなにかしらのアプリと絡めて使う感じなんですね。( ´_ゝ`)ふーん