Step by step guide to configure and query DynamoDB with GoLang

Recently I wanted to use dynamoDB for one of my go service so I went through lots of blogs to see all the steps involved in this process but I could not find anything so I thought of writing this blog so that others do not have to go through the pain ,which I did :)

Without wasting too much time , lets jump on steps:-

  1. Before you start connection to DynamoDB , you need to have some table created in AWS. So Go to AWS and open DynamoDB service . Create table “person” which has following JSON structure
{
“PersonSSN” : “11111111”
“FirstName” : “David”
“LasName” : “Lal”
“Email” : “abcd@jhandoo.com
}

2. Make sure you have added required accessKey or AWS credentials to your profile in .aws directory.

3. For Go program, please import “github.com/aws/aws-sdk-go” to your project.

4. In Go program, in order to establish session with AWS dynamoDB instance, please use following line of code:-

sess, err := session.NewSession(&aws.Config{
Region: aws.String(“us-west-2”),
Credentials: credentials.NewSharedCredentials(“/Users/someone/.aws/credentials”, “YOUR_AWS_PROFILE_NAME”),
})
svc := dynamodb.New(sess)

You can see, it depends on aws credentials which you added as part of step2. There is a way to hard code the credentials, but that is not recommended way of creating session as it has potential security risk of exposing your environment to hackers.`

5. If you want to test and see, if you can fetch details related to a table which you created in AWS environment . You can use following lines of code:-

 req := &dynamodb.DescribeTableInput{
TableName: aws.String(“person”),
}
result, err := svc.DescribeTable(req)
if err != nil {
fmt.Printf(“%s”, err)
}
table := result.Table
fmt.Printf(“done”, table)

Above line of code will print the details about person table which you created as part of step 1.`

6. If you want to fetch all the items added for person table then you can probably use scan api. Remember this is not efficient way of fetching records as per AWS, so if you want to fetch one item based on key then probably use query api. Though here is scan api example

 params := &dynamodb.ScanInput{
TableName: aws.String(“person”),
}
result, err := svc.Scan(params)
if err != nil {
fmt.Errorf(“failed to make Query API call, %v”, err)
}

In order to unmarshal whole resultSet to some struct you can use following line of code.


obj := []Person{}err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &obj)
if err != nil {
fmt.Errorf(“failed to unmarshal Query result items, %v”, err)
}

Following will printout the whole object , this is just for testing,If you have lots of data, then probably don’t try this.


fmt.Println(obj)

7. As I mentioned in step 5 , in order to filter content based on one key you can use query. Lets say you had added personSSN as primary key, while creating person table and you want to find an item having personSSN = 1111 then use following:-

var queryInput = &dynamodb.QueryInput{
TableName: aws.String(“person”),
IndexName: aws.String(“personSSN”),
KeyConditions: map[string]*dynamodb.Condition{
“personSSN”: {
ComparisonOperator: aws.String(“EQ”),
AttributeValueList: []*dynamodb.AttributeValue{
{
S: aws.String(“1111”),
},
},
},
},
}
var resp1, err1 = svc.Query(queryInput)
if err1 != nil {
fmt.Println(err1)
} else {
personObj := []Person{}
err = dynamodbattribute.UnmarshalListOfMaps(resp1.Items, &personObj)
log.Println(personObj)
}

8. If you want to filter content based on a different key which is not primary key lets say for firstName, then go to AWS console and add firstName as GSI. Once you do that please come back to go program and add following line of code :-

var queryInput = &dynamodb.QueryInput{
TableName: aws.String("person"),
IndexName: aws.String("firstName-index"),
KeyConditions: map[string]*dynamodb.Condition{
"modifier": {
ComparisonOperator: aws.String("EQ"),
AttributeValueList: []*dynamodb.AttributeValue{
{
S: aws.String("David"),
},
},
},
},
}
var resp1, err1 = svc.Query(queryInput)
if err1 != nil {
fmt.Println(err1)
} else {
personObj := []Person{}
err = dynamodbattribute.UnmarshalListOfMaps(resp1.Items, &personObj)
log.Println(personObj)
}

9. Lets say above returns multiple person objects and you want to restrict DynamoDB to return only one record then you can use limit parameter with queryInput.

var queryInput = &dynamodb.QueryInput{
Limit: aws.Int64(1),
TableName: aws.String("person"),
IndexName: aws.String("firstName-index"),
KeyConditions: map[string]*dynamodb.Condition{
"modifier": {
ComparisonOperator: aws.String("EQ"),
AttributeValueList: []*dynamodb.AttributeValue{
{
S: aws.String("David"),
},
},
},
},
}
So far you learned, how to fetch all the items or use query to filter results and limit number of results. But if you want to filter DynamoDB items based on some startIndex and endIndex, then remember DynamoDB doesn’t support the same. As dynamo DB is good for basically key based search.

10. There is one last trick, lets say if you want to find out how many tables your AWS dynamoDB instance contains then you can use ListTablesPages to fetch the same. Here is code to achieve the same.


obj2 := dynamodb.ListTablesInput{
ExclusiveStartTableName: aws.String(“person”),
Limit: aws.Int64(1),
}
pageNum := 0
// Example iterating over at most 3 pages of a ListTables operation.
err10 := svc.ListTablesPages(&obj2,
func(page *dynamodb.ListTablesOutput, lastPage bool) bool {
fmt.Println(“I found myself”, page)
pageNum++
fmt.Println(“I found you too”, page)
return pageNum <= 5
})
if err10 != nil {
fmt.Println(“FKU Dynamo”)
}

12. Above steps should help you to write any kind of DynamoDB based Go queries or program. But if you still need anything else, then please feel free to post your question here.