あめがえるのITブログ

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

ブラウザのURLからAPI Gateway(REST API)+Lambda(Python)経由でDynamoDBを操作してみた 第2回

第1回ではGETはできるようになったのでPOSTやDELETEもやってみた。 amegaeru.hatenablog.jp しかし、ただメソッドを作成すればよい話ではなさそう。。 (´Д`。)

◆問題点

ブラウザのURLから投げられるHTTPリクエストはGETのみのため同じURLではメソッドによる処理の振り分けができない

◆解決案

下記ができそう( ̄ ~  ̄
 案1.処理ごとにリソースを分ける
 案2.URLの引数に処理用の値を加える

◆実践!

案1.処理ごとにリソースを分ける

 1-1.まずはAPIGatewayで処理ごとのリソースとメソッドを作成する。
  下記のように作成。
   /
    create(リソース)
     └GET(メソッド)
    read(リソース)
     └GET(メソッド)
    delete(リソース)
     └GET(メソッド)
 
 この場合メソッドはすべてGETのためメソッドで処理を分けれないのでLambdaを複数作成する必要がありだるい。。
 Lambdaプロキシ統合ではHTTPリクエストを生成してくるので、その中の[resource]は振り分けに使えそう。

HTTPリクエスト
{
    "resource": "/read",
    "path": "/",
    "httpMethod": "GET",
    "headers": {
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
        "accept-encoding": "gzip, deflate, br",
・・・

 1-2.Lambdaコード編集
  HTTPリクエストから[resource]を取る形に修正

import json
import boto3

# DynamoDB クライアントの作成
dynamodb = boto3.client('dynamodb')
table_name = 'sample-dynamodb'

def lambda_handler(event, context):
    method = event["resource"]
    
    if method == "/create":
        return create(event, context)
    elif method == "/read":
        return read(event, context)
    elif method == "/update":
        return update(event, context)
    elif method == "/delete":
        return delete(event, context)
    else:
        return {
            "statusCode": 400,
            "body": json.dumps({"error": "Unsupported method"})
        }
        
def create(event, context):
    id = event["queryStringParameters"]["id"]
    value = event["queryStringParameters"]["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["queryStringParameters"]["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["queryStringParameters"]["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"})
        }

 1-3.動作確認
  下記にブラウザからアクセスして各処理ができることを確認
  1.Read
   https://bsm44pfp4j.execute-api.ap-northeast-1.amazonaws.com/test/read?id=123

{"Item": {"id": {"S": "123"}, "value": {"S": "test"}}, "ResponseMetadata": {"RequestId": "U4T46FMF6DAHGI6ALDOBUJ1BBBVV4KQNSO5AEMVJF66Q9ASUAAJG", "HTTPStatusCode": 200, "HTTPHeaders": {"server": "Server", "date": "Sun, 07 May 2023 12:32:09 GMT", "content-type": "application/x-amz-json-1.0", "content-length": "48", "connection": "keep-alive", "x-amzn-requestid": "U4T46FMF6DAHGI6ALDOBUJ1BBBVV4KQNSO5AEMVJF66Q9ASUAAJG", "x-amz-crc32": "2920716868"}, "RetryAttempts": 0}}

  2.Create
    https://bsm44pfp4j.execute-api.ap-northeast-1.amazonaws.com/test/create?id=124&value=test2

{"ResponseMetadata": {"RequestId": "5R4RI8OL791HAD2LSLTPO97NMFVV4KQNSO5AEMVJF66Q9ASUAAJG", "HTTPStatusCode": 200, "HTTPHeaders": {"server": "Server", "date": "Sun, 07 May 2023 12:32:38 GMT", "content-type": "application/x-amz-json-1.0", "content-length": "2", "connection": "keep-alive", "x-amzn-requestid": "5R4RI8OL791HAD2LSLTPO97NMFVV4KQNSO5AEMVJF66Q9ASUAAJG", "x-amz-crc32": "2745614147"}, "RetryAttempts": 0}}

  3.Delete
    https://bsm44pfp4j.execute-api.ap-northeast-1.amazonaws.com/test/delete?id=124&value=test2

{"ResponseMetadata": {"RequestId": "AS89KR80P3U2RNKKQHP435I3U3VV4KQNSO5AEMVJF66Q9ASUAAJG", "HTTPStatusCode": 200, "HTTPHeaders": {"server": "Server", "date": "Sun, 07 May 2023 12:32:57 GMT", "content-type": "application/x-amz-json-1.0", "content-length": "2", "connection": "keep-alive", "x-amzn-requestid": "AS89KR80P3U2RNKKQHP435I3U3VV4KQNSO5AEMVJF66Q9ASUAAJG", "x-amz-crc32": "2745614147"}, "RetryAttempts": 0}}

※DynamoDB側でも作成、削除されていることを確認
 [DynamoDB]-[テーブル]-[sample-dynamodb]-[テーブルアイテムの検索]

.
.

案2.URLの引数に処理用の値を加える

  引数にmethodを加える形でやっていきます。
  例)https://xxxx.com/test?id=123&method=read

 2-1.まずはAPIGatewayで処理を一手に引き受けるメソッドを作成する。
  下記のように作成。
   /
   └GET(メソッド)

 2-2.Lambdaコード編集
  URLのmethod値をもとに処理を振り分ける形に修正。

import json
import boto3

# DynamoDB クライアントの作成
dynamodb = boto3.client('dynamodb')
table_name = 'sample-dynamodb'

def lambda_handler(event, context):
    method = event["queryStringParameters"]["method"]
    
    if method == "create":
        return create(event, context)
    elif method == "read":
        return read(event, context)
    elif method == "delete":
        return delete(event, context)
    else:
        return {
            "statusCode": 400,
            "body": json.dumps({"error": "Unsupported method"})
        }
        
def create(event, context):
    id = event["queryStringParameters"]["id"]
    value = event["queryStringParameters"]["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["queryStringParameters"]["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["queryStringParameters"]["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"})
        }

 2-3.動作確認
  下記にブラウザからアクセスして各処理ができることを確認
  1.Read
   https://bsm44pfp4j.execute-api.ap-northeast-1.amazonaws.com/test?id=123&method=read

{"Item": {"id": {"S": "123"}, "value": {"S": "test"}}, "ResponseMetadata": {"RequestId": "U6AJS5LTQDQHEQKBRCCBMK161NVV4KQNSO5AEMVJF66Q9ASUAAJG", "HTTPStatusCode": 200, "HTTPHeaders": {"server": "Server", "date": "Sun, 07 May 2023 11:01:59 GMT", "content-type": "application/x-amz-json-1.0", "content-length": "48", "connection": "keep-alive", "x-amzn-requestid": "U6AJS5LTQDQHEQKBRCCBMK161NVV4KQNSO5AEMVJF66Q9ASUAAJG", "x-amz-crc32": "2920716868"}, "RetryAttempts": 0}}

  2.Create
    https://bsm44pfp4j.execute-api.ap-northeast-1.amazonaws.com/test?id=124&value=test2&method=create

{"ResponseMetadata": {"RequestId": "KI4CFMFA205A3NSP0KJP90B2G3VV4KQNSO5AEMVJF66Q9ASUAAJG", "HTTPStatusCode": 200, "HTTPHeaders": {"server": "Server", "date": "Sun, 07 May 2023 11:50:34 GMT", "content-type": "application/x-amz-json-1.0", "content-length": "2", "connection": "keep-alive", "x-amzn-requestid": "KI4CFMFA205A3NSP0KJP90B2G3VV4KQNSO5AEMVJF66Q9ASUAAJG", "x-amz-crc32": "2745614147"}, "RetryAttempts": 0}}

  3.Delete
    https://bsm44pfp4j.execute-api.ap-northeast-1.amazonaws.com/test?id=124&value=test2&method=delete

{"ResponseMetadata": {"RequestId": "6JK26S8FNN66QR616V9NS32GT7VV4KQNSO5AEMVJF66Q9ASUAAJG", "HTTPStatusCode": 200, "HTTPHeaders": {"server": "Server", "date": "Sun, 07 May 2023 11:51:32 GMT", "content-type": "application/x-amz-json-1.0", "content-length": "2", "connection": "keep-alive", "x-amzn-requestid": "6JK26S8FNN66QR616V9NS32GT7VV4KQNSO5AEMVJF66Q9ASUAAJG", "x-amz-crc32": "2745614147"}, "RetryAttempts": 0}}

※DynamoDB側でも作成、削除されていることを確認
 [DynamoDB]-[テーブル]-[sample-dynamodb]-[テーブルアイテムの検索]

◆結論

案2のほうがあれこれ作らなくてよいからいいかなと思いました。インフラ目線・・・

◆感想

HTTP APIがないときはこんな検討していたのか、そもそもこんなことしないのか。。。
まぁいい勉強になったヾ(o´∀`o)ノ
put_itemだけJSONの中身が違うのがはまった、、、