DynamoDB: Exercise 3.1: Get/Add Items to DynamoDB Tables

Kerry Sheldon
9 min readJun 24, 2018

--

(This post is part of the AWS for the Bootcamp Grad series. The series consists of exercises to build familiarity with various aspects of the AWS ecosystem. Again, all of these posts are “exercises” for introductory exposure to AWS — they are NOT represented as best practices.)

Background

This is the first exercise on DynamoDB — a NoSQL database service provided by AWS. DynamoDB allows you to create non-relational database tables, using key-value data structures.

In this exercise, you will create two DynamoDB tables — using the two different types of primary keys (single attribute and composite key). You will learn to scan and query a Dynamo table using the AWS SDK.

You will also create a static website hosted on S3 as an interface to add items to the DynamoDB tables, and to retrieve items from the tables using different types of queries. To do that, you will write a browser-side script that uses the AWS SDK and authentication through Amazon Cognito.

Specifically, you will do the following:

  • Create a DynamoDB table with a single attribute key.
  • Create a DynamoDB table with a composite key.
  • Add items to each table using the AWS console.
  • Create an Amazon Cognito identity pool (your browser script will need to provide credentials to access your DynamoDB resources).
  • Configure the Amazon Cognito’s IAM role for unauthenticated users to provide access to your DynamoDB tables.
  • Create an S3 bucket and configure it to host a static website.
  • Create a browser side script to list and display all items from a table.
  • Create a browser side script to list all items with a certain partition key value.
  • Create a browser side script to add items to the tables.

Create a DynamoDB table with a Single Attribute Key

In this exercise, you will be creating a website that allows you to create flashcards grouped by topics — and to view those flashcards in a browser. For the sake of the exercise, you will create two DynamoDB tables — topics and flashcards — so you can utilize both types of primary keys supported by DynamoDB.

As described in the AWS documentation, DynamoDB supports the following primary key types:

Partition key: Also known as a hash key, the partition key is composed of a single attribute. Attributes in DynamoDB are similar in many ways to fields or columns in other database systems.

Partition key and sort key: Referred to as a composite primary key or hash-range key, this type of key is composed of two attributes. The first attribute is the partition key, and the second attribute is the sort key.

The topics table will contain a single attribute: name. As such, you will use a single-attribute key for this table.

From the DynamoDB dashboard, click Create Table. Enter ‘topics’ as the table name, and ‘name’ as the partition key (type: String). Click Create.

Create a DynamoDB table with a Composite Key

The flashcards table will contain four attributes: topic, front, back, and createdAt. Your primary use case will be retrieving all flashcards with a given topic name. Each items in this table will have a topic value — and that topic value will also be stored in the topics table you just created.

Because you will be getting items from this table by topic, you will set topic as the partition key (this will allow you to do a query operation, rather than a less performant scan operation). However, because topic will not be a unique key on this table, you must utilize a composite key by adding a sort key.

You could use front as the sort key. However, this would preclude you from having more than one card with the same front value under a given topic. To avoid that restriction, you can use the createdAt unix timestamp as a sort key, solely for the purpose of establishing a unique primary key.

From the DynamoDB dashboard, click Create Table. Enter ‘flashcards’ as the table name, and ‘topic’ as the partition key (type: String). Click to add a sort key. Enter ‘createdAt` as the sort key and select Number from the dropdown as the data type. Click Create.

Add Items to Each Table Using the AWS Console

From the DynamoDB dashboard, click Tables from the sidebar on the left. Select topics from the table list. Then, click the Items tab. Click Create Item. Enter ‘Spanish’ as the value for the name attribute and click Save.

Now, select flashcards from the table list, click the Items tab and click Create Item. Enter ‘Spanish’ as the value for topic and enter any number as the value for createdAt (you will use a unix timestamp in the future for this attribute).

Click the plus sign to add another attribute. Select append, and choose String as the datatype. Enter ‘front’ as the field, and ‘hello’ as the value. Repeat that process to add a ‘back’ field with a value of ‘hola’. Click Save.

You will notice that you were required to enter the primary key field(s). Moreover, you were able to add any additional field/value combination to an item in your tables.

Create an Amazon Cognito Identity Pool and Configure IAM Role

Follow the Create an Amazon Cognito Identify Pool and Configure IAM Role section of Exercise 2.3. However, you will need to use the following as the policy for the unauthenticated role. You will need to use the arn values for your tables in the resource section below:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:*"
],
"Resource": [
"<your-flashcard-table-arn>",
"<your-topic-table-arn>"
]
}
]
}

This policy will allow anyone using this role to perform any DynamoDB action on either of your two tables.

Create An S3 Bucket and Configure it for Website Hosting

Follow the Create an S3 Bucket and Configure the Bucket to Host a Static Website sections of Exercise 2.1.

Create a Script to Upload and List All Files

In this exercise, you will be uploading two objects to the website bucket you just created — index.html and app.js. The app.js file will be the browser-side script that performs actions on the DynamoDB tables.

Create an index.html file with the following:

<html>
<head>
<!-- Javascript AWS SDK -->
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.247.1.min.js"></script>
<!-- Your Browser-Side script -->
<script src="./app.js"></script>
<!-- Latest compiled and minified Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Inline CSS - primarily for flashcard functionality --> <style>
.btn-info {
margin-right: 10px
}
.flashcard-container {
-webkit-perspective: 1000;
}
.flashcard {
height: 100px;
margin: 10% auto;
border: 2px solid darkgray;
border-radius: 4px;
box-shadow: 2px 2px 2px darkgray;
-webkit-transform-style: preserve-3d;
transition: all 0.3s;
-webkit-transition: all 0.3s;
}
.flashcard:hover{
border: 3px solid lightgray;
cursor: pointer;
}
.flipped, .back {
transform: rotateX(180deg);
-webkit-transform: rotateX(180deg);
}
.front, .back {
position: absolute;
-webkit-backface-visibility: hidden;
height: 90px;
}
.front p, .back p {
padding: 10;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<!-- create a flashcard -->
<form id="card-form">
<div class="form-row">
<div class="col-md-3">
<input id="topic-input" class="form-control" type="text" placeholder="Topic">
</div>
<div class="col-md-4">
<input id="front-input" class="form-control" type="text" placeholder="Front">
</div>
<div class="col-md-4">
<input id="back-input" class="form-control" type="text" placeholder="Back">
</div>
<div class="col-md-1">
<button type='button' class="btn btn-info" onclick="addCard()">Add</button>
</div>
</div>
</form>
</div>
<div class="row">
<!-- list of all flashcard topics -->
<div class="col-md-4">
<h4>Flashcard Topics</h4>
<div id="topic-content"></div>
</div>
<!-- display flashcards for selected topic -->
<div class="col-md-8">
<div id="flashcard-content"></div>
</div>
</div>
</div>
<script>
//calls a method on your browser-side script - app.js
listTopics();
</script>
</body>
</html>

Create an app.js file with listTopics, addCard, and showCards functions (the addCard and showCards functions will be implemented in future steps). Be sure to update the script with your region and Cognito Identity Pool information.

The listTopics function will use the JavaScript AWS SDK’s scan functionality (see the DynamoDB class documentation here):

const flashCardTable = 'flashcards';
const topicTable = 'topics';
//insert pool id of your congito pool
const IdentityPoolId = '<your-pool-id>';
const credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId })
//insert your region
const region = 'us-east-1';
AWS.config.update({
region,
credentials
});
const ddb = new AWS.DynamoDB({
apiVersion: '2012-10-08'
});
function addCard() {
console.log('to be implemented')
}
function showCards() {
console.log('to be implemented')
}
function listTopics() {
clearFlashCards()
clearTopicList()
const params = {
"TableName": topicTable
};

//performs a scan operation to get all items from the topics table
//because you are not searching based on a primary key value, this uses a less-performant scan operation rather than a query
ddb.scan(params, function(err, data) {
if (err) {
return alert('Error: ' + err.message);
} else {
const topicHtml = data.Items.map(function(item) {
const topic = item.name.S
return getTopicHtml(topic)
}).join('')
topicContainer().innerHTML = topicHtml
}
});
}
function getTopicHtml(topic) {
return `<p>
<button class="btn btn-info btn-xs" data-topic="${topic}"
onClick="showCards(event)">Show</button>
${topic}</p>`
}
//misc helper functionsfunction topicContainer() {
return document.getElementById('topic-content')
}
function flashCardContainer() {
return document.getElementById('flashcard-content')
}
function clearFlashCards() {
flashCardContainer().innerHTML = ""
}
function clearTopicList() {
topicContainer().innerHTML = ""
}
function toggleFlipped(event) {
const flashCardElement = event.target.closest('.flashcard')
flashCardElement.classList.toggle('flipped')
}

Upload both files to your website bucket (granting public read permission to both).

Visit your website URL and upload the file you just created. You should see the ‘Spanish’ topic name listed.

Create a Script to List all Items with a Certain Partition Key Value

In this step, you will implement the showCards function that is called when the show button is clicked. This method will utilize the DynamoDBquery method. Update the showCards function on your app.js as follows:

function showCards(event) {
//get the topic name from the event
const topic = event.target.dataset.topic
const params = {
"TableName": flashCardTable, //table you are going to query
"ExpressionAttributeValues": {
":topic" : {"S": topic} //the field and value that you want to query by (including its data type "S" - string)
},
"ExpressionAttributeNames": {
"#B": "back" //because back is a reserve word, you need to alias it
},
"KeyConditionExpression": "topic = :topic",
"ProjectionExpression": "front, #B" //the attributes you want returned
};

ddb.query(params, function(err, data) {
if (err) {
return alert('Error: ' + err.message);
} else {
clearFlashCards()
const numCards = data.Count
const cards = data.Items
const flashCardHtml = cards.map(function(card) {
const front = card.front.S
const back = card.back.S
return getFlashCardHtml(front, back)
}).join('')
const topicTitle = `<h4>Topic: ${topic} (${numCards} cards)</h4>`
flashCardContainer().innerHTML = topicTitle + flashCardHtml
}
})
}

This documentation explains parameter options for the query operation. This query was complicated by the fact thatback is a reserve word in DynamoDB . As such it needed to be aliased using the ExpressionAttributeNames param.

Also, add the getFlashCardHtml function below:

function getFlashCardHtml(front, back) {
return `<div class="flashcard-container">
<div class="flashcard" onClick="toggleFlipped(event)">
<span class="glyphicon glyphicon-repeat" aria-hidden="true"></span>
<div class="front">
<p>${front}</p>
</div>
<div class="back">
<p>${back}</p>
</div>
</div>
</div>`
}

Upload the new app.js file to your website bucket (grant public read permission). Navigate to your website URL, refresh the page. When you click Show, you should now see your first flashcard displayed.

Create a Script to Add Items to the Tables

In this step you will implement the addCard method. This method will make use of the batchWriteItem method on the DynamoDB SDK. This method allows you to create multiple items, on multiple tables using a single request. Because you will to add items to the topics and flashcards tables simultaneously, this is a good choice. You can read the documentation here.

Update your app.js file by changing the addCard method to the following:

function addCard() {
const topic = document.getElementById("topic-input").value
const front = document.getElementById("front-input").value
const back = document.getElementById("back-input").value
const params = {
"RequestItems": {
"topics": [ //params for the topics item
{
"PutRequest": {
"Item": {
"name": {
"S": topic
}
}
}
}
],
"flashcards": [ //params for the flashcards item
{
"PutRequest": {
"Item": {
'topic': {"S": topic},
'front': {"S": front},
'back': {"S": back},
'createdAt': {"N": Date.now().toString()}
}
}
}
]
}
}
ddb.batchWriteItem(params, function(err, data) {
if (err) {
return alert('Error: ' + err.message);
} else {
document.getElementById('card-form').reset()
listTopics()
}
})
}

Upload the new app.js file to your website bucket (grant public read permission). Navigate to your website URL, refresh the page.

Add a new card to the Spanish topic using the form. Then click Show next to the topic name. You should see your new card displayed.

Add a new card to a new topic of ‘French’. You will see the new topic listed, and the card will be visible when you click the Show button.

Clean Up

Navigate to your S3 dashboard. Click the bucket symbol next to the website bucket name. Then click Delete Bucket. Type the name of the bucket, and click Confirm.

Delete your Cognito identity pool following the instructions shown here. Delete your Dynamo DB tables by selecting the table from the list on the dashboard and clicking Delete Table.

--

--

Kerry Sheldon

Software Developer. Graduate of Turing School of Software and Design.