Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang, and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand.
I recently wrote a tutorial on creating an AWS Lambda function that communicated with the Couchbase NoSQL database using the Go programming language. This previous tutorial was based on a standard serverless development, but what if we wanted to take things to the next level? For example, Amazon Alexa-powered devices, in most circumstances, leverage AWS Lambda to function. So what would it take to make our function compatible with Alexa?
In this tutorial we’re going to see how to work with Amazon Alexa requests and respond appropriately so that Alexa will relay an audio message back to the user. The data we respond with will come from our database and it will be managed with the Go programming language.
Creating a Lambda Function with Amazon Alexa Support
If you haven’t seen my previous tutorial, I suggest you take a look before reading further. The previous tutorial is short and will provide some perspective into developing Lambda functions with Go and Couchbase.
Moving forward, create a project within your **$GOPATH** with a **main.go** file in it. From the command line, execute the following to download our dependencies:
1 2 3 4 5 6 7 |
go get github.com/arienmalec/alexa-go go get github.com/aws/aws-lambda-go/lambda go get github.com/satori/go.uuid go get gopkg.in/couchbase/gocb.v1 |
Of our dependencies, we are downloading the AWS Lambda SDK for Go as well as the Couchbase SDK for Go. We are also downloading a UUID package for generating unique values to represent our document keys, which is what we had in our previous tutorial. However, this time around we are downloading another package that contains models for each of our requests and responses. Alexa expects both requests and responses to be in a certain JSON format, which is pretty much the only purpose of the Alexa package.
In the **main.go** file, add the following boilerplate code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
package main import ( "github.com/arienmalec/alexa-go" "github.com/aws/aws-lambda-go/lambda" uuid "github.com/satori/go.uuid" gocb "gopkg.in/couchbase/gocb.v1" ) var bucket *gocb.Bucket type Todo struct { ID string `json:"id,omitempty"` Text string `json:"text,omitempty"` Type string `json:"type,omitempty"` } func IntentDispatcher(request alexa.Request) (alexa.Response, error) { var response alexa.Response switch request.Body.Intent.Name { case "GetTodosIntent": response = HandleGetTodosIntent(request) case "CreateTodoIntent": response = HandleCreateTodoIntent(request) case "AboutIntent": response = HandleAboutIntent(request) } return response, nil } func HandleAboutIntent(request alexa.Request) alexa.Response { } func HandleCreateTodoIntent(request alexa.Request) alexa.Response { } func HandleGetTodosIntent(request alexa.Request) alexa.Response { } func main() { cluster, _ := gocb.Connect("couchbase://HOST_HERE") cluster.Authenticate(gocb.PasswordAuthenticator{Username: "todos", Password: "123456"}) bucket, _ = cluster.OpenBucket("todos", "") lambda.Start(IntentDispatcher) } |
So what is happening in the above code?
After importing the downloaded dependencies, we can create a Todo
data structure which will hold our data. Similar to the previous example, we are going to be storing todo list information and querying that information.
In the main
function we are establishing a connection to our Couchbase instance and opening our bucket. It is important that our Couchbase instance be hosted somewhere because Lambda needs to be able to interact with it. This means you probably can’t test with Couchbase running on your local host.
When it comes to Alexa, things happen based on actions known as intents. These actions are typically a request to Alexa. For any given Skill you can typically interact with Alexa in numerous ways, hence numerous intents. Our Skill will have an AboutIntent
for information, a CreateTodoIntent
for saving data, and a GetTodosIntent
for querying our data and returning it to the user. These intents will be controlled by our IntentDispatcher
function.
The idea behind our IntentDispatcher
is a request will come in from Alexa. Within the request, Amazon will figure out the correct intent and we can use that information. This intent information will be defined later on. Using the intent information we can call the correct function.
So let’s look at the AboutIntent
function:
1 2 3 4 5 |
func HandleAboutIntent(request alexa.Request) alexa.Response { return alexa.NewSimpleResponse("About", "Created by Nic Raboy of The Polyglot Developer.") } |
If the function is called, we can use the Alexa package to format our response as correct JSON and return it. The response will have a title and text to be spoken back to the user.
Now let’s look at the CreateTodoIntent
function:
1 2 3 4 5 6 7 8 9 |
func HandleCreateTodoIntent(request alexa.Request) alexa.Response { todo := Todo{Text: request.Body.Intent.Slots["todo"].Value} bucket.Insert(uuid.Must(uuid.NewV4()).String(), todo, 0) return alexa.NewSimpleResponse("Todo Created", "Added `"+todo.Text+"` to the list.") } |
If our dispatcher chooses the above function, we will take the dynamic data, known as a slot, and insert it into Couchbase. The slot data will be parsed by Amazon and included in the request, just like the intent. The slot data will be returned and spoken by Alexa when we’re done.
The final function is longer, but not much more complicated:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
func HandleGetTodosIntent(request alexa.Request) alexa.Response { todos := "" query := gocb.NewN1qlQuery("SELECT text FROM `todos` AS todos") rows, _ := bucket.ExecuteN1qlQuery(query, nil) var row Todo for rows.Next(&row) { todos += row.Text + ". " } return alexa.NewSimpleResponse("Todo List", todos) } |
In the HandleGetTodosIntent
we are querying the open bucket using N1QL. With the results we are forming a string of data to be returned to the user and spoken by Alexa.
With the code complete, we can focus on the deployment.
Deploying and Testing the Lambda Function as an Alexa Skill
Before we can deploy to the Skill store, we need to prepare our function code for Lambda. Similar to the previous example, we need to cross compile our application for Linux which is what Lambda uses.
From the command line, execute the following:
1 2 3 |
GOOS=linux go build zip handler.zip ./binary-name |
Make sure to change the binary-name
with that of the name of your actual binary file created from the previous build command. If you can’t run the zip
command, go ahead and archive the file however makes the most sense.
With the **handler.zip** file in hand, go to the Lambda Management Dashboard (https://console.aws.amazon.com/lambda) and create a new function using the defaults. Just make sure you’re using a Go project.
For the trigger, choose **Alexa Skills Kit** and proceed to upload your ZIP file. For the **Handler** make sure you provide the name of your binary file, not the name of your ZIP file.
The last thing to take note of is your ARN id. This ARN value will be needed when we configure Alexa.
Now go to the Alexa Developer Console (https://developer.amazon.com/alexa/console) so we can create a new Skill. As part of the creation process, you’ll need to do four things:
- Invocation Name
- Intents, Samples, and Slots
- Build Model
- Endpoint
The invocation name doesn’t have to match your Skill name, but it should be real words that are pronounceable in all accents. If you start making up words or are using complicated words, good luck with Alexa understanding what people said.
The endpoint is the ARN that you copied in the previous step from the Lambda console. Make sure you paste it in.
Most of our work will be around creating intents, sample utterances, and our dynamic slot data. We need to create three intents to match what we have in our Golang code. Each of our intents should have a list of phrases that activate the intent.
For example, the GetTodosIntent
might have the following sample utterances:
1 2 3 4 5 6 7 |
what are my todos get me my list what is on my todo list get me my todos |
The more example phrases you have, the better your Skill will perform. Basically, the samples help Alexa determine which intent to trigger in your code. The CreateTodoIntent
is where things will get a little complicated because we are expecting dynamic data.
Take a look at the following sample utterances:
1 2 3 4 5 |
add {todo} to my list save {todo} create {todo} for my list |
Notice that I have {todo}
in the above samples which matches that of my slot in my code. The {todo}
placeholder is actually a variable for dynamic information. This information will be saved to our database. However, we need to define what type of data {todo}
is.
Create a custom slot type and give it a few values. For example, I added:
1 2 3 |
wash car clean house |
You don’t need an exhaustive list, but it is learning data for Alexa, andhelps Alexa identify what types of information should be considered slot information. Just because text doesn’t exist in the list doesn’t mean Alexa won’t pick it up.
At this point in time, you should be able to build your Skill. Go ahead and type in some sample phrases in the test portal to see it in action before you try to deploy.
Conclusion
You just saw how to create an Amazon Alexa Skill using Golang and Couchbase Server. This was an extension to a previous tutorial I wrote titled, Developing AWS Lambda Functions with Golang and Couchbase NoSQL.
There is also something to note about our Skill. Amazon has several mandatory intents that must be present in order to be approved. For example, there need to be intents for stopping and cancelling actions. We didn’t add those, but they would follow the same strategy.
If you’d like to see another example of developing a Skill with Golang, check out my tutorial titled, Build an Alexa Skill with Golang and AWS Lambda.