How To Implement A Value Counter With DynamoDB Streams

Using DynamoDB streams and a Lambda function

Overview

A common use case in a NoSQL database is to keep an aggregated count or pre-computed value on your database.

For example, you can keep a count of how many likes are on a post, or number of attendees at an event.

Rather than querying a list of items, an aggregated value provides a much more efficient and faster way of tracking a count value.

In this post we’ll take a look at implementing this with DynamoDB Streams and an AWS Lambda function.

Enabling DynamoDB Streams

The first step here is to enable Streams in your DynamoDB table.

In the Exports and streams tab, scroll to the bottom of that page to find the streams section (at the bottom).

Click on turn on to enable streams.

On this page you can select new image. This will let us receive a stream of the data after it was written to our table

Implementing the counter with AWS Lambda

Now that we’ve enabled streams, we can use a Lambda function to react to these changes on our DynamoDB table.

Here’s the architecture we will build:

  • A Lambda function will connect to our DynamoDB table

  • A DynamoDB change stream — a new attendee item added — will trigger this function

  • The function will then update an item on the table — increment a count of total attendees.

We’ll start by creating a new Lambda function.

I’ll call the function “increment-events-count”. I’m using the Node JS runtime and the arm64 architecture.

In the permissions section, add a role to allow full dynamodb access to this function (Check out this guide for help).

Once created, let’s add some code in the code editor below.

Let’s add the following code to react to new item writes to our table and update a value within a single function:

import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
import { DynamoDBDocumentClient, UpdateCommand } from '@aws-sdk/lib-dynamodb'

const client = new DynamoDBClient({})
const ddbDoc = DynamoDBDocumentClient.from(client)

export const handler = async (event) => {
  try {
    const inserts = event.Records.filter(
      (record) => record.eventName === 'INSERT'
    )
    for (const record of inserts) {
      const newItem = record.dynamodb.NewImage
      if(!newItem.attendeeID.S) continue;

      await ddbDoc.send(
        new UpdateCommand({
          TableName: 'events',
          KeyConditionExpression: { 
            pk:  { S: "event#101" },
            sk: { S: "info" },
          },
          UpdateExpression: 'ADD #count :incr',
          ExpressionAttributeNames: {
            '#count': 'count',
          },
          ExpressionAttributeValues: {
            ':incr': 1,
          },
        })
      )
    }
    return { statusCode: 200 }
  } catch (err) {
    console.error('Error processing DynamoDB stream:', err)
    throw err;
  }
}

Our code is accepting a change stream event from our DynamoDB and checks if the new item has a “attendeeID” attribute. If it does, it will increment a count value on the event item to keep track of attendees to that event.

Now we can add a DynamoDB trigger to this function.

Click on the add trigger button.

Here we can select DynamoDB from the dropdown in trigger configuration and select our DynamoDB table (events).

We can now save the trigger.

With that we can now test our solution.

Let’s start adding items to our events table.

We’ll add one event item, representing our actual event and one event attendee item to represent a new attendee on the event.

Adding an event item:

Adding an event attendee:

When we add this attendee item, we will see the event item above has an updated “count” value incremented to 1.

You now have an automated aggregated counter.

You can of course improve it by adding functionality for delete events, where you would decrease the counter by 1 on attendee item deletes.

Conclusion

By combining DynamoDB streams with an AWS Lambda function we can efficiently create real-time aggregate values like attendee counts for events.

This serverless approach ensures scalablity and low latency while keeping the counter data consistent and reliable.

👋 My name is Uriel Bitton and I hope you learned something in this edition of Excelling With DynamoDB.

📅 If you're looking for help with DynamoDB, let's have a quick chat.

🙌 I hope to see you in next week's edition!