Photo by frank mckenna on Unsplash

What is Amazon DynamoDB

It’s a fully managed, non-relational database. You won’t need to spin up any database servers, install patches or worry about scaling and disk space. DynamoDB does it all for you. It will also replicate your data across three separate data centres giving you amazing data durability and service availability.

Superb Freebies

DynamoDB is a generous soul. It won’t charge you a penny for your first 25 GB worth of indexed data. Nor do you pay for your first few million requests each month (depending on the amount of data read or written for each request).

Enough Talk, Let Us Tinker

Assuming you have an AWS account with a default profile setup locally, then you’ll be able to create your first table in a matter of seconds:

Conjuring Spells from the Command Line

We can access and manage DynamoDB from the command line (CLI) with Amazon’s CLI tool

Managing your tables from the CLI can be verbose, so it helps readability to break longer statements on to multiple lines with the backward slash.

Creating a Table

We’re busy and important, so it would be handy to have somewhere to store a list of tasks for the day, to help keep us organised. So let’s create a table for that.

aws dynamodb create-table \
  --table-name Todos \
  --attribute-definitions \
    AttributeName=id,AttributeType=N \
  --key-schema \
    AttributeName=id,KeyType=HASH \
  --provisioned-throughput \
    ReadCapacityUnits=1,WriteCapacityUnits=1

What just happened?

We created a table called Todos, with the create-table operation, and we passed several options: first, we have --table-name, used unsurprisingly for defining the table’s name. Then, --attribute-definitions and --key-schema, which are necessary for defining the table’s primary key.

This table has a ‘simple’ primary key, in that it consists of a single part - the partition key (also called the hash key). DynamoDB also supports composite primary keys, made up of a partition key and a sort key - read more here.

Next, we define the table’s throughput capacity (--provisioned-throughput) for write and read operations. One read capacity unit can read up to 8 KB per second and one write capacity unit can write up to 1 KB per second. These values aren’t fixed when the table is created and can be increased or decreased as demand changes. DynamoDB also supports auto scaling, where your table’s throughput is managed for you, depending on how busy your table is.

Let’s confirm the table was created with the list-tables operation:

aws dynamodb list-tables

# output
{
    "TableNames": [
        "Todos"
    ]
}

Creating a Todo Item

With our table ready, let’s create our first todo.

aws dynamodb put-item \
  --table-name Todos \
  --item \
    '{
      "id": {"N": "1"},
      "description": {"S": "Feed the pet tortoise."},
      "completed": {"BOOL": false}
    }' \
  --return-consumed-capacity TOTAL

What just happened?

DynamoDB is schema-less, so the number of attributes and their types can vary across items in a table. The one required attribute is the primary key, and this must be unique across all items in a table.

With the --item option, we passed the item’s attributes and values as JSON. Notice that for each attribute we have a name and an object containing the value and its type:

  "Key": {"Type": "Value"}

  # e.g.
  "completed": {"BOOL": false}

DynamoDB items support various attribute types, including number (N), string (S), boolean (BOOL), binary (B) and null (NULL). Collection types are also supported - set (number, string and binary), list, and map.

Reading an Item

With the get-item operation we can read an item with a particular primary key.

aws dynamodb get-item \
  --table-name Todos \
  --key '{"id":{"N":"1"}}' \
  --return-consumed-capacity TOTAL

The response:

{
    "Item": {
        "completed": {
            "BOOL": true
        },
        "description": {
            "S": "Feed the pet tortoise."
        },
        "id": {
            "N": "1"
        }
    },
    "ConsumedCapacity": {
        "CapacityUnits": 0.5,
        "TableName": "Todos"
    }
}

What about reading multiple items with one query? For that we can use the scan operation. First, let’s create a second todo item:

aws dynamodb put-item \
  --table-name Todos \
  --item \
    '{
      "id": {"N": "2"},
      "description": {"S": "Pet the horse"},
      "completed": {"BOOL": false}
    }' \
  --return-consumed-capacity TOTAL

With scan we can read all of our todos:

aws dynamodb scan \
  --table-name Todos \
  --return-consumed-capacity TOTAL

And the response:

{
    "Count": 2,
    "Items": [
        {
            "completed": {
                "BOOL": false
            },
            "description": {
                "S": "Pet the horse"
            },
            "id": {
                "N": "2"
            }
        },
        {
            "completed": {
                "BOOL": true
            },
            "description": {
                "S": "Feed the pet tortoise."
            },
            "id": {
                "N": "1"
            }
        }
    ],
    "ScannedCount": 2,
    "ConsumedCapacity": {
        "CapacityUnits": 0.5,
        "TableName": "Todos"
    }
}

We’ve used scan at its most simplest. It’s a sophisticated operation, and deserves a blog post of its own. For example, scan supports filter expressions, that allow you to describe a subset of items you want returned.

Updating an Item

The pet tortoise has been fed, so let’s update the item as completed:

aws dynamodb update-item \
  --table-name Todos \
  --key \
    '{
      "id": {"N": "1"}
     }' \
  --update-expression "SET completed = :done" \
  --expression-attribute-values \
  '{
    ":done": {"BOOL": true}
   }' \
   --return-values ALL_NEW \
   --return-consumed-capacity TOTAL

The operation, update-item, updates an item on table named Todos (--table-name TodoItems). with a matching primary key, defined by --key.

Next, we define our update expression.

--update-expression "SET completed = :done" \
--expression-attribute-values \
'{
  ":done": {"BOOL": true}
 }'

We want to SET the completed attribute to the value we assign to :done. We define what :done is with --expression-attribute-values; :done’s type is BOOLean and its value is true.

--return-values ALL_NEW tells DynamoDB to return the item as it looks after the update.

Here’s the response to running this command:

{
    "Attributes": {
        "completed": {
            "BOOL": true
        },
        "description": {
            "S": "Feed the pet tortoise."
        },
        "id": {
            "N": "1"
        }
    },
    "ConsumedCapacity": {
        "CapacityUnits": 1.0,
        "TableName": "Todos"
    }
}

Deleting an Item

You remember that you don’t have a tortoise, so decide to delete the todo item:

aws dynamodb delete-item \
  --table-name Todos \
  --key \
    '{
      "id": {"N": "1"}
    }' \
    --return-consumed-capacity TOTAL

Now, when we scan the table we get:

aws dynamodb scan \
  --table-name Todos \
  --return-consumed-capacity TOTAL

# response
{
    "Count": 1,
    "Items": [
        {
            "completed": {
                "BOOL": false
            },
            "description": {
                "S": "Pet the horse"
            },
            "id": {
                "N": "2"
            }
        }
    ],
    "ScannedCount": 1,
    "ConsumedCapacity": {
        "CapacityUnits": 0.5,
        "TableName": "Todos"
    }
}

Our little dally with DynamoDB is almost over - there’s just one operation left to execute…

Deleting a Table

Deleting our Todos table couldn’t be easier…

aws dynamodb delete-table --table-name Todos

Let’s check it’s gone…

aws dynamodb list-tables

# response
{
    "TableNames": []
}