🐉 AWS CDK 101 - 🏇 Using batched dynamodb stream to delete item on another dynamodb table
Tags: aws, serverless, typescript, cdk
Published: 2022-05-14 07:27:10

:beginner: Beginners new to AWS CDK, please do look at my previous articles one by one in this series.

If in case missed my previous article, do find it with the below links.

:repeat: Original previous post at :link: [Dev Post](https://devpost.hashnode.dev/aws-cdk-101-fetching-json-from-dynamodb-vs-s3-through-stepfunction)

:repeat: Reposted previous post at :link: [dev to @aravindvcyber](https://dev.to/aravindvcyber/aws-cdk-101-fetching-json-from-dynamodb-vs-s3-through-stepfunction-1mb7)

In this article, let us add a new async batch integration  to our message dynamodb table which will help us to delete the processed records from the staging dynamodb table.

## Benefits in this approach :sweat_drops:

* In this approach we tried to clear the staging table data, asynchronously using the dynamodb stream directly invoking a lambda.
* So we can use this in systems where we may not directly do the scavenging synchronous in a compute-intensive workload. Saving compute by avoiding wait time for these I/O.
* Again, some may argue that anyway I am making a lambda call elsewhere separately, which could be thought of as a good decoupling strategy as well.
* But invocation charges and compute still apply there. Yet, we need no one more thing that the streams can be ready in batches by our lambda and the default is 100, so one invocation.
* At the same time, the handler can do a batch request to delete the data from the dynamodb in a single request.
* So not only you can read a batch of streams, but you can also perform batch delete which, is fast as well as we get this delete operation into chunks of 25 max limit which gives a great reduction in the number of I/O operations from lambda.
* Maybe I will write a separate article later about it, for now, it is not limited to the 1:1 delete operation here.

https://dev.to/aravindvcyber/aws-cdk-101-dynamodb-stream-triggering-batch-deleteitem-on-dynamodb-table-2p8l

![batch size](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pzj2jap0qsk3vbq1d829.png)

![batched stream](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x870wesbdgovx2x6ohpq.png)

## Planning :lady_beetle:

Here we will be making use of the dynamodb streams to trigger the `deleteItem` action by invoking a new lambda handler function which achieves our objective.

## Construction :white_flower:

We need a new lambda function code that has a fresh handler to receive the dynamodb stream event object and process it as shown below.

Create a new lambda function in the file `lambda/message-stream.ts`.

Here you may find that we are targeting an event name to be `INSERT`, likewise we can have finer control over our desired outcome during these stream invocations as shown below.

## New handler function logic :stuffed_flatbread:

```ts
exports.created = async function (event: any) {
  console.log("Received stream:", JSON.stringify(event, undefined, 2));
  const results: any[] = [];
  await Promise.all(
    event.Records.map(async (Record: any) => {
      console.log(JSON.stringify(Record, undefined, 2));
      if (Record.eventName === "INSERT") {
        results.push(await deleteDbItem(Record.dynamodb.Keys));
      }
    })
  );
  results.map((res) => console.log(res));
};
```

## Helper function dynamodb deleteItem :bouquet:

Simple helper function to perform deleteItem from a dynamodb table.

```ts
const deleteDbItem: any = async (keys: any) => {
  console.log("Deleting: ", { keys });
  const deleteData: DeleteItemInput = {
    TableName: process.env.STAGING_MESSAGES_TABLE_NAME || "",
    Key: keys,
    ReturnConsumedCapacity: "TOTAL",
  };
  console.log("deleteItem: ", JSON.stringify(deleteData, undefined, 2));
  return await dynamo.deleteItem(deleteData).promise();
};

```

## Minor changes to the dynamodb table definition :mandarin:

I have highlighted the necessary changes, we need to perform dynamodb stream generation for our table.

Most importantly, I have requested both the new and old images, which will have all the necessary data, however, we are not going to use all of the data. This is only for demonstration purposes.

```ts
const messages = new dynamodb.Table(this, "MessagesTable", {
      tableName: process.env.messagesTable,
      sortKey: { name: "createdAt", type: dynamodb.AttributeType.NUMBER },
      partitionKey: { name: "messageId", type: dynamodb.AttributeType.STRING },
      encryption: dynamodb.TableEncryption.AWS_MANAGED,
      readCapacity: 5,
      writeCapacity: 5,
      //New item added below
      stream: dynamodb.StreamViewType.NEW_AND_OLD_IMAGES
});
```
![db stream](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tuq472yagj2hk4gmw1tg.png)


## Defining the new lambda :grapes:

Here we will use the code bloc used above to provision our lambda as shown below inside our master stack.

```ts
const messageStreamFunc = new lambda.Function(this, "messageStreamFunc", {
      runtime: lambda.Runtime.NODEJS_14_X,
      code: lambda.Code.fromAsset("lambda"),
      handler: "message-stream.created",
      logRetention: logs.RetentionDays.ONE_MONTH,
      tracing: Tracing.ACTIVE,
      layers: [nodejsUtils,nodejsXray],
      environment: {
        STAGING_MESSAGES_TABLE_NAME: envParams.messages.stgTableName || "",
      },
});
messageStreamFunc.applyRemovalPolicy(RemovalPolicy.DESTROY);
```
![Function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i6xbm25g0tcsca9c9u84.png)

![trigger](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/prsjoll0q60q1btl4rq6.png)

## Grant permission for the handler to write to the table :dash:

Also, our handler must have sufficient access to delete from the other dynamodb table  `stgMessages`. 

```ts
stgMessages.grantWriteData(messageStreamFunc);

```

![db permission](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nxqcbl10b1tq4jwzd87h.png)


## Adding Event source to the lambda function :corn:

It is time not to connect the handler function to the dynamodb event source as follows.

```ts
messageStreamFunc.addEventSource(
    new DynamoEventSource(messages, {
    startingPosition: lambda.StartingPosition.LATEST,
    })
)
```

or you can improve the batch-size and window in seconds for long polling as follows.

```ts
messageStreamFunc.addEventSource(new DynamoEventSource(messages, {
      startingPosition: lambda.StartingPosition.LATEST,
      batchSize:100,
      maxBatchingWindow: Duration.seconds(60)
}))
```

## Sample dynamodb stream object 🥣

I have shared the dynamodb stream object used as payload to invoke our handler lambda below.

```json

{
  "Records": [
    {
      "eventID": "5c9aa5395f970324e088a32578ee0a66",
      "eventName": "INSERT",
      "eventVersion": "1.1",
      "eventSource": "aws:dynamodb",
      "awsRegion": "ap-south-1",
      "dynamodb": {
        "ApproximateCreationDateTime": 1652355068,
        "Keys": {
          "createdAt": {
            "N": "1652355039000"
          },
          "messageId": {
            "S": "47d97141-01e7-42a1-b6b3-0c59a6a3827e"
          }
        },
        "NewImage": {
          "createdAt": {
            "N": "1652355039000"
          },
          "messageId": {
            "S": "47d97141-01e7-42a1-b6b3-0c59a6a3827e"
          },
          "event": {
            "S": "{\n    \"message\": {\"new\": \"A secret message\"}\n}"
          }
        },
        "SequenceNumber": "20927100000000035334874026",
        "SizeBytes": 173,
        "StreamViewType": "NEW_AND_OLD_IMAGES"
      },
      "eventSourceARN": "arn:aws:dynamodb:ap-south-1:8888888:table/MessagesTable/stream/2022-05-*****"
    }
  ]
}
```

## Console log during execution :popcorn:

Finally post-execution, we could find the above JSON payload we have received in the `event` object and which is then used to delete from our staging table you may find the results below in cloud watch logs.

![deleting](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rydd2cpz4k52cyie3pm1.png)

In the next article, we will demonstrate how we will use a similar approach to delete Object from S3, which we have previously created.

We will be adding more connections to our stack and making it more usable in the upcoming articles by creating new constructs, so do consider following and subscribing to my newsletter.

⏭ We have our next article in serverless, do check out

https://dev.to/aravindvcyber/aws-cdk-101-dynamodb-stream-triggering-batch-deleteitem-on-dynamodb-table-2p8l

🎉 Thanks for supporting! 🙏

Would be great if you like to [☕ Buy Me a Coffee](https://www.buymeacoffee.com/AravindVCyber), to help boost my efforts.

<a href="https://www.buymeacoffee.com/AravindVCyber"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=AravindVCyber&button_colour=BD5FFF&font_colour=ffffff&font_family=Cookie&outline_colour=000000&coffee_colour=FFDD00" /></a>

<a href='https://ko-fi.com/X8X0CITDJ' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://cdn.ko-fi.com/cdn/kofi4.png?v=3' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>

:repeat: Original post at 🔗 [Dev Post](https://devpost.hashnode.dev/aws-cdk-101-using-batched-dynamodb-stream-to-delete-item-on-another-dynamodb-table#write-comment)

:repeat: Reposted at 🔗 [dev to @aravindvcyber](https://dev.to/aravindvcyber/aws-cdk-101-fetching-json-from-dynamodb-vs-s3-through-stepfunction-1mb7)


<blockquote class="twitter-tweet"><p lang="en" dir="ltr">🐉 AWS CDK 101 - 🏇 Using batched dynamodb stream to delete item on another dynamodb table<br> <a href="https://twitter.com/hashnode?ref_src=twsrc%5Etfw">@hashnode</a><br><br>Checkout more like this in my page<a href="https://t.co/CuYxnKr0Ig">https://t.co/CuYxnKr0Ig</a><a href="https://twitter.com/hashtag/TheHashnodeWriteathon?src=hash&amp;ref_src=twsrc%5Etfw">#TheHashnodeWriteathon</a><a href="https://twitter.com/hashtag/awscdk?src=hash&amp;ref_src=twsrc%5Etfw">#awscdk</a> <a href="https://twitter.com/hashtag/aws?src=hash&amp;ref_src=twsrc%5Etfw">#aws</a> <a href="https://twitter.com/hashtag/awslambda?src=hash&amp;ref_src=twsrc%5Etfw">#awslambda</a> <a href="https://twitter.com/hashtag/thwcloud?src=hash&amp;ref_src=twsrc%5Etfw">#thwcloud</a>-computing <a href="https://t.co/RlrahA0wrI">https://t.co/RlrahA0wrI</a></p>&mdash; Aravind V (@Aravind_V7) <a href="https://twitter.com/Aravind_V7/status/1525380566291709952?ref_src=twsrc%5Etfw">May 14, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>