Getting Started With Dokklib-DB for DynamoDB Single Table
Tags: aws, dynamodb, serverless, python
Published: 2020-02-27 20:55:41

## DynamoDB Single Table

Dokklib-DB is a [Python library for the DynamoDB single table pattern.](https://dokklib.com/libs/db/) If you can anticipate your query patterns, DynamoDB is a particularly good fit for serverless applications, as it has minimal operational overhead and stateless connections. (Read more about [DynamoDB vs SQL for serverless](https://dokklib.com/guides/aws/dynamodb#dynamodb-vs-sql)).

With the single table pattern, you put all your data that you want to perform relational queries on into one DynamoDB table. The key to a successful single table design is being able to anticipate query patterns, as you have to structure the data accordingly. Let's see how this works in practice with Dokklib-DB!

## Install Dokklib-DB

Install Dokklib-DB and Boto3 with Pip (you need Python 3.6 or later):

`pip install "boto3>=1.10.34,<2" dokklib-db`

## Table Setup

We need a table with a generic composite primary key. We will call the partition key `PK` and the sort key `SK`. The generic key names allow us to store different entity types in the same table. We also a need a global secondary index that is the inverse of the primary index (ie. where the partition key is `SK` and the sort key is `PK`). We will use this inverse index for many-to-many relational queries. You can read more about the [single table setup in the Dokklib-DB docs](https://dokklib.com/libs/db/table-setup/) or see a [complete CloudFormation template here.](https://github.com/dokklib/dokklib-db/blob/master/tests/integration/cloudformation.yml)

## Define Entities

Suppose that we want to be able to add our users to groups and make many-to-many relational queries to fetch a user's groups or the members of a group. First, we define the user and group entities:


```python
import dokklib_db as db


class User(db.EntityName):
    """User entity name.

    Key value: unique user name, eg. 'alice'.
    Example key: 'USER#alice'.

    """


class Group(db.EntityName):
    """Group entity name.

    Key value: unique group name, eg. 'my-group'.
    Example key: 'GROUP#my-group'.

    """
```

These classes don't hold any data, they just provide type-safety and documentation for our entities.

## Insert Data

Let's see how we can add a user to a group:

```python
table = db.Table('SingleTable')

# Construct entity keys

pk_alice = db.PartitionKey(User, 'alice')
print(str(pk_alice))
# USER#alice

sk_group_1 = db.SortKey(Group, '1')
print(str(sk_group_1))
# GROUP#1

# Add Alice to group one
table.insert(pk_alice, sk_group_1)

```

Suppose that we added another user and a second group and we have the following data in the table:

PK             | SK         
-------------- | ---------- 
USER#alice     | GROUP#1    
USER#alice     | GROUP#2    
USER#bob       | GROUP#2    


# Many-to-Many Queries

Now we can use the primary index to query all the groups of a user:


```python
# Get all the groups that Alice belongs to

pk_alice = db.PartitionKey(User, 'alice')
print(str(pk_alice))
# USER#alice

group_prefix = db.PrefixSortKey(Group)
print(str(group_prefix))
# GROUP#

alice_groups = table.query_prefix(pk_alice, group_prefix)
print(alice_groups)
# [{'SK': '1'}, {'SK': '2'}]
```

And the inverse index to query all the members of a group:

```python
# Get all users in group two

pk_group_2 = db.PartitionKey(Group, '2')
print(str(pk_group_2))
# GROUP#2

user_prefix = db.PrefixSortKey(User)
print(str(user_prefix))
# USER#

inverse_index = db.InversePrimaryIndex()

members = table.query_prefix(pk_group_2, user_prefix, 
    global_index=inverse_index)
print(members)
# [{'PK': 'alice'}, {'PK': 'bob'}]

```

This wraps up our short intro. I encourage you to continue exploring in the [Dokklib-DB docs](https://dokklib.com/libs/db/) and to provide feedback/feature requests on [Github.](https://github.com/dokklib/dokklib-db)