In this post I invite you to build an API with Golang programming language and use the DynamoDB service. But all in our Local environment. Trust me ? zero cost for using AWS Cloud Services, let’s get started !

AWS and Localstack

Many times we need to test an AWS service without spending our time in infrastructure and without having to incur in expenses, we must have a tool that allows us to create a local environment and thus be able to perform the necessary tests in a homologous way as we do in our cloud provider, in this case, AWS Services.

The good news is that we have a solution to achieve this, its name is localstack, this tool will allow us to generate a local environment quickly without worrying about the time it takes to create the infrastructure and the expenses generated by the use of AWS services.

To learn how to use LocalStack we are going to implement a REST microservice developed with the Golang programming language, the objective of this API will be to insert and query data in DynamoDB without using the AWS cloud.

What is LocalStack ?

To begin, let’s talk about LocalStack, LocalStack is a tool that will allow us to emulate AWS services in a local environment, till the date localstack in its free version allows us to use DynamoDB locally among others services.

This is achieved by provisioning LocalStack + Docker, AWS services will be dockerized and ready to be used through an endpoint, e.g. DynamoDB will be accessed through:

http://localhost:4566

Install Localstack

The localstack installation will be done through brew, with brew a docker image will be downloaded that will contain a mock up of the AWS services.

$ brew install localstack

To initialize it, it will be necessary:

$ localstack start

With this we will have our AWS services ready to be consumed.

Now, in order to consume the DynamoDB service in a local environment we can do it by console, but why not do it with a programming language like Golang, we will learn how to do it:

Access to DynamoDB with Golang

First we are going to talk about our DB, DynamoDB is a non-relational database, for the example we are going to save two columns, Key and Value, thus approving a table that will allow us to store and consult parameters.

The database can be easily created using infrastructure as code with Terraform:

Before creating the resources we must configure our provider to point to our local environment:

# local
terraform {
    backend "local" {}
}

provider "aws" {
    access_key                  = "mock_access_key"
    region                      = "us-east-1"
    s3_force_path_style         = true
    secret_key                  = "mock_secret_key"
    skip_credentials_validation = true
    skip_metadata_api_check     = true
    skip_requesting_account_id  = true

    endpoints {
        dynamodb    = "http://0.0.0.0:4566"
    }
}

Now we can create our AWS resource, in this case our DynamoDB table called ParameterAPI:

resource "aws_dynamodb_table" "basic-dynamodb-table" {
    name           = "ParameterAPI"
    billing_mode   = "PROVISIONED"
    read_capacity  = 20
    write_capacity = 20
    hash_key       = "Key"

    attribute {
        name = "Key"
        type = "S"
    }

    ttl {
        attribute_name = "TimeToExist"
        enabled        = false
    }

    tags = {
        Name        = "dynamodb-table-ParameterAPI"
        Environment = "development"
    }
}

Let’s insert and consult with Golang

To access DynamoDB we have to instantiate a client creating the session with the configuration of the endpoint and the region, in our example we are returning an interface called DynamoDBAPI as a standard and recommendation must be used because with it we can mock and implement our unit tests with ease:

func NewSession(region string) dynamodbiface.DynamoDBAPI {
    endpoint := "http://localhost:4566"

    // Create Dynamodb AWS session
    config := &aws.Config{
        Endpoint: &endpoint,
        Region:   &region,
    }

    var svc dynamodbiface.DynamoDBAPI
    sess := session.Must(session.NewSession(config))
    svc = awsDynamo.New(sess)

    return svc
}

Teniendo creada la sesion vamos a guardar un parametro:

func (d DynamoService) Save(item model.Item) error {

    input := &dynamodb.PutItemInput{
        Item: map[string]*dynamodb.AttributeValue{
            "Key": {
                S: aws.String(item.Key),
            }, "Value": {
                S: aws.String(item.Value),
            },
        },
        TableName: aws.String(d.tableName),
    }
    _, err := d.dynamoDBAPI.PutItem(input)

    if err != nil {
        return err
    }

    return nil
}

Let’s Code

Github repository: https://github.com/edcab/poc-localstack-dynamo-golang