Search This Blog

Monday 25 May 2020

Dynamo Db APIs through API Gateway

In the last post, we setup a GET method for my table through API Gateway. I wanted to go ahead and setup the other methods - DELETE, PUT, POST.
Ill start with the PUT method. PUT call on "/persons" endpoint is used to add a record in my table
The put Method is to be transformed into a PutItem API call on DynamoDb.

Accordingly I setup my mapping template in the IntegrationRequest Layer:
{
    "TableName": "Person",
    "Item": {
        "pId": {
            "S": "$context.requestId"
        },
        "pName": {
            "S": "$util.escapeJavaScript($input.params('name'))"
        },
        "age": {
           "N": "$input.params('age')"
        },
        "creationTime" : {
            "S":"$context.requestTime"
        }
    }
}    
This will create an entry using the name and age fields provided by user in request url. I needed the 'id' to be unique. I could either take the id from user and depend on the conditional check or I could use a system created unique identifier. Dynamo Db does not provide table sequences similar to Oracle. So I need to pass the unique identifier here. For this I leveraged the unique request Id generated by API Gateway. This requestId is available in the context object.
On testing my method the APIG logs are as below:

Execution log for request 852a5371-b157-4a72-91f5-8e7a0317e0fe
Mon May 25 17:24:05 UTC 2020 : Starting execution for request: 
852a5371-b157-4a72-91f5-8e7a0317e0fe
Mon May 25 17:24:05 UTC 2020 : HTTP Method: PUT, Resource Path: /persons
Mon May 25 17:24:05 UTC 2020 : Method request path: {}
Mon May 25 17:24:05 UTC 2020 : Method request query string: {name=Foo Bar, age=28}
Mon May 25 17:24:05 UTC 2020 : Method request headers: {}
Mon May 25 17:24:05 UTC 2020 : Method request body before transformations: 
Mon May 25 17:24:05 UTC 2020 : Endpoint request URI: 
https://dynamodb.us-east-1.amazonaws.com/?Action=PutItem
Mon May 25 17:24:05 UTC 2020 : Endpoint request headers: {
Authorization=******a4cf74,
X-Amz-Date=20200525T172405Z, x-amzn-apigateway-api-id=qbotcgknke, 
Accept=application/json, User-Agent=AmazonAPIGateway_qbotcgknke, 
X-Amz-Security-Token=IQoJb3JpZ [TRUNCATED]
Mon May 25 17:24:05 UTC 2020 : Endpoint request body after transformations: {
    "TableName": "Person",
    "Item": {
        "pId": {
            "S": "852a5371-b157-4a72-91f5-8e7a0317e0fe"
        },
        "pName": {
            "S": "Foo Bar"
        },
        "age": {
           "N": "28"
        },
        "creationTime" : {
            "S":"25/May/2020:17:24:05 +0000"
        }
    }
}    
      
Mon May 25 17:24:05 UTC 2020 : 
Sending request to https://dynamodb.us-east-1.amazonaws.com/?Action=PutItem
Mon May 25 17:24:05 UTC 2020 : Received response. Status: 200, Integration latency: 23 ms
Mon May 25 17:24:05 UTC 2020 : Endpoint response headers: {Server=Server, 
Date=Mon, 25 May 2020 17:24:05 GMT, Content-Type=application/x-amz-json-1.0, 
Content-Length=2, Connection=keep-alive, 
x-amzn-RequestId=S9HLQJMK9I85EPHO821DHSKLOJVV4KQNSO5AEMVJF66Q9ASUAAJG, 
x-amz-crc32=2745614147}
Mon May 25 17:24:05 UTC 2020 : Endpoint response body before transformations: {}
Mon May 25 17:24:05 UTC 2020 : Method response body after transformations: {}
Mon May 25 17:24:05 UTC 2020 : Method response headers: {
X-Amzn-Trace-Id=Root=1-5ecbff35-1b8bb0370fcd46fe20912834, 
Content-Type=application/json}
Mon May 25 17:24:05 UTC 2020 : Successfully completed execution
Mon May 25 17:24:05 UTC 2020 : Method completed with status: 200
As seen from logs, PutItem does not return a response. So I have the problem of communicating my unique identifier back in response. This does make user id with validations look to be a better method.

My remaining 3 methods - GET/Update/Delete operate at id resource. We saw GET in the previous post.

{
    "TableName": "Person",
    "PrimaryKey": "pId",
    "KeyConditionExpression": "pId = :v1",
    "ExpressionAttributeValues": {
        ":v1": {
            "S": "$input.params('id')"
        }
    }
}
The mapping template will read the id from request url and use it for a Query. Since we did not want to return the dynamodb JSON, but a simpler version, I setup a (Velocity) mapping template in my IntegrationResponse layer:
#set($inputRoot = $input.path('$'))
{
#if($inputRoot.Count >= 1)   
    "id": "$inputRoot.Items[0].pId.S",
    "name": "$inputRoot.Items[0].pName.S",
    "age": "$inputRoot.Items[0].age.N"
#end    
}
Next is the Delete method. This accepts only the Person identifier which the IntegrationRequest layer converts into a DynamoDb DeleteItem API call.

The MappingTemplate to convert to a DynamoDb DeleteItem call is as below:
{
    "TableName": "Person",
    "Key": {
        "pId": {
            "S": "$input.params('id')"
        }
    },
    "ReturnValues": "ALL_OLD"
}
This will return the Dynamo Db record if it exists. If we do not want dynamo db JSON to be passed through we need to setup a IntegrationResponse mapping template (similar to what we did in GET method) I tested the method and the logs are:
Execution log for request 3f7e59b3-05f7-41ac-9479-a59a79ab7397
Mon May 25 17:44:19 UTC 2020 : Starting execution for request: 3f7e59b3-05f7-41ac-9479-a59a79ab7397
Mon May 25 17:44:19 UTC 2020 : HTTP Method: DELETE, Resource Path:
 /persons/f85fc54c-0119-4dc8-98f0-4a644b074557
Mon May 25 17:44:19 UTC 2020 : Method request path: {id=f85fc54c-0119-4dc8-98f0-4a644b074557}
Mon May 25 17:44:19 UTC 2020 : Method request query string: {}
Mon May 25 17:44:19 UTC 2020 : Method request headers: {}
Mon May 25 17:44:19 UTC 2020 : Method request body before transformations: 
Mon May 25 17:44:19 UTC 2020 : Endpoint request URI: https://dynamodb.us-east-1.amazonaws.com/?Action=DeleteItem
Mon May 25 17:44:19 UTC 2020 : Endpoint request headers: {Authorization=****3cb1e4, X-Amz-Date=20200525T174419Z, 
x-amzn-apigateway-api-id=qbotcgknke, Accept=application/json, User-Agent=AmazonAPIGateway_qbotcgknke, 
X-Amz-Security-Token=IQoJb3JpZ2luX2VjED [TRUNCATED]
Mon May 25 17:44:19 UTC 2020 : Endpoint request body after transformations: {
    "TableName": "Person",
    "Key": {
        "pId": {
            "S": "f85fc54c-0119-4dc8-98f0-4a644b074557"
        }
    },
    "ReturnValues": "ALL_OLD"
}
Mon May 25 17:44:19 UTC 2020 : Sending request to 
https://dynamodb.us-east-1.amazonaws.com/?Action=DeleteItem
Mon May 25 17:44:19 UTC 2020 : Received response. Status: 200, Integration latency: 26 ms
Mon May 25 17:44:19 UTC 2020 : Endpoint response headers: {Server=Server, Date=Mon, 25 May 2020 17:44:18 GMT, 
Content-Type=application/x-amz-json-1.0, Content-Length=158, Connection=keep-alive, x-amzn-RequestId=MSO723GT56G3QF2QDKHN9PQ8Q3VV4KQNSO5AEMVJF66Q9ASUAAJG, x-amz-crc32=4050537793}
Mon May 25 17:44:19 UTC 2020 : Endpoint response body before transformations:
 {"Attributes":{"pId":{"S":"f85fc54c-0119-4dc8-98f0-4a644b074557"},
"pName":{"S":"Foo Bar"},"creationTime":{"S":"25/May/2020:17:09:27 +0000"},
"age":{"N":"35"}}}
Mon May 25 17:44:19 UTC 2020 : Method response body after transformations: 
{"Attributes":{"pId":{"S":"f85fc54c-0119-4dc8-98f0-4a644b074557"},
"pName":{"S":"Foo Bar"},"creationTime":{"S":"25/May/2020:17:09:27 +0000"},
"age":{"N":"35"}}}
Mon May 25 17:44:19 UTC 2020 : Method response headers: {X-Amzn-Trace-Id=Root=1-5ecc03f3-cb333de7541ff8c3f8aebeb6,
 Content-Type=application/json}
Mon May 25 17:44:19 UTC 2020 : Successfully completed execution
Mon May 25 17:44:19 UTC 2020 : Method completed with status: 200
The last method is the POST method which was used to update an Item. The method uses the Id to identify the item, allowing user to provide name and age values.

The mapping template for Integration Request is:
{
    "TableName": "Person",
    "Key": {
        "pId": {
            "S": "$input.params('id')"
        }
    },
    "UpdateExpression" : "SET pName = :pName, age = :age, updationTime = :utime",
    "ExpressionAttributeValues": {
        ":pName" : { "S":"$util.escapeJavaScript($input.params('name'))" },
        ":age":  { "N": "$input.params('age')"},
        ":utime":{ "S" : "$context.requestTime"}
    },
    "ReturnValues": "ALL_NEW"
}
On hitting the url:
Execution log for request 55e87c6e-3998-43a5-8523-ef936c9acb44
Mon May 25 18:17:24 UTC 2020 : Starting execution for request: 
55e87c6e-3998-43a5-8523-ef936c9acb44
Mon May 25 18:17:24 UTC 2020 : HTTP Method: POST, 
Resource Path: /persons/52a5371-b157-4a72-91f5-8e7a0317e0fe
Mon May 25 18:17:24 UTC 2020 : Method request path: 
{id=52a5371-b157-4a72-91f5-8e7a0317e0fe}
Mon May 25 18:17:24 UTC 2020 : Method request query string: 
{name=Bar Bar, age=31}
Mon May 25 18:17:24 UTC 2020 : Method request headers: {}
Mon May 25 18:17:24 UTC 2020 : Method request body before transformations: 
Mon May 25 18:17:24 UTC 2020 : Endpoint request URI: 
https://dynamodb.us-east-1.amazonaws.com/?Action=UpdateItem
Mon May 25 18:17:24 UTC 2020 : Endpoint request headers: {
Authorization=***d7c400, X-Amz-Date=20200525T181724Z,
x-amzn-apigateway-api-id=qbotcgknke, Accept=application/json, User-Agent=
AmazonAPIGateway_qbotcgknke,  X-Amz-Security-Token=IQoJb3JpZ2lu [TRUNCATED]
Mon May 25 18:17:24 UTC 2020 : Endpoint request body after transformations: {
    "TableName": "Person",
    "Key": {
        "pId": {
            "S": "52a5371-b157-4a72-91f5-8e7a0317e0fe"
        }
    },
    "UpdateExpression" : "SET pName = :pName, age = :age, updationTime = :utime",
    "ExpressionAttributeValues": {
        ":pName" : { "S":"Bar Bar" },
        ":age":  { "N": "31"},
        ":utime":{ "S" : "25/May/2020:18:17:24 +0000"}
    },
    "ReturnValues": "ALL_NEW"
}
Mon May 25 18:17:24 UTC 2020 : Sending request to 
https://dynamodb.us-east-1.amazonaws.com/?Action=UpdateItem
Mon May 25 18:17:24 UTC 2020 : Received response. Status: 200, Integration latency: 24 ms
Mon May 25 18:17:24 UTC 2020 : Endpoint response headers: {Server=Server, 
Date=Mon, 25 May 2020 18:17:24 GMT, Content-Type=application/x-amz-json-1.0,
 Content-Length=157, Connection=keep-alive, 
 x-amzn-RequestId=H3CT55IRA94BPAQ973EAREL46VVV4KQNSO5AEMVJF66Q9ASUAAJG,
 x-amz-crc32=1694387638}
Mon May 25 18:17:24 UTC 2020 : Endpoint response body before transformations:
 {"Attributes":{"pId":{"S":"52a5371-b157-4a72-91f5-8e7a0317e0fe"},
"updationTime":{"S":"25/May/2020:18:17:24 +0000"},"pName":{"S":"Bar Bar"},
"age":{"N":"31"}}}
Mon May 25 18:17:24 UTC 2020 : Method response body after transformations:
 {"Attributes":{"pId":{"S":"52a5371-b157-4a72-91f5-8e7a0317e0fe"},
"updationTime":{"S":"25/May/2020:18:17:24 +0000"},"pName":{"S":"Bar Bar"},
"age":{"N":"31"}}}
Mon May 25 18:17:24 UTC 2020 : Method response headers: {X-Amzn-Trace-Id=Root
=1-5ecc0bb4-66d6e13e89875088eb2ff333, Content-Type=application/json}
Mon May 25 18:17:24 UTC 2020 : Successfully completed execution
Mon May 25 18:17:24 UTC 2020 : Method completed with status: 200

1 comment: