Alexa Skills with Go
The introduction of Go for AWS Lambda provides significant advantages for writing Lambdas. In particular, Go for AWS Lambda has strong cold-start and runtime performance.
Because Alexa skills are called unpredictably, the cold start benefits make Go an attractive language for writing Alexa skills. As I’ve been playing around with both Go and Alexa, I wanted to write an end to end implementation of a reasonably sophisticated Alexa skill in Go with automated deployment. Unfortunately, due to the lack of tutorials, I had to figure out much of the mechanics myself. This guide documents what I discovered.
The goal here is to build an Alexa skill that says “Hello, world” in multiple languages, and also reponds to the Alexa help intent. This will require both understanding the Alexa request, and making the appropriate Alexa responses. Because this focuses on the AWS Lambda Go hander side of the Alexa skill, this tutorial does not document the Alexa Skill Kit Developer Console experience, for which there are plenty of tutorials.
To wire up automated deployment, I first wrote the simplest Go-based Lambda, and used the CloudFormation Serverless Application Model to automate deployment. There were a few wrinkles I discovered along the way. The Go code for the AWS Lambda is:
AWS Lambda with Go automatically marshals response structs via
encoding/json which makes Go-based Lambdas quite clean.
Deploying this manually is pretty simple — you follow the AWS instructions for compiling an AWS Lambda-compliant Go executable, zip the executable, and upload to the Lambda in the console. However, from experience, doing this repeatedly is a chore and automating this is helpful.
I created the following CloudFormation Serverless Application Model template:
Most of this is Go independent. However, the
Handler and the
CodeUri properties require explaination. The
Hander is the name of the Go executable (unlike in other AWS Lambda language implementations where this might be a method), and the
CodeUri is the on-disk zip file that contains the Go executable. In the package CloudFormation step, the zip file is uploaded to the S3 bucket
hello_lambda (which must first be created), and the deploy template is mapped to the zip file in S3. When deployed, the created AWS Lambda uses the S3 deployed zipped executable.
The zip file must contain only the
hello executable file, with no directory paths. If the zip file is created incorrectly (for example, if you accidentally zip the executable with a directory path), you will not receive a friendly error, either on the package or deploy step, and if you test the resulting AWS Lambda you will get an extremely cryptic error message:
"errorMessage": "fork/exec /var/task/hello: no such file or directory",
Note that the default package step without an explicit path will zip a directory. While that might be helpful for other languages, for Go, even if that directory contains your executable, this will not work.
Here’s the script I created to automate compling, zipping, packaging and deploying the AWS Lambda:
hello an Alexa skill, we need to add two things:
- Return the appropriate Alexa skill response
- Add the Alexa skill event to the CloudFormation template
The response needs to accord to the specification of the Alexa response JSON protocol. The simplest possible response will return just
outputSpeech and end. As an interim hack, I’ve modified the
Response struct to conform to the JSON Alexa response model for the simplest case. This is obviously inflexible, but will work for now. Adding the Alexa skill event to CloudFormation is much simpler.
The resulting code and CloudFormation template are now:
If we create a simple Alexa skill and wire the skill to the resulting Lambda, it all works.
To handle intents and locales, we need to add the request object. This gets pretty verbose to handle inline in the Lambda, so I created an
alexa package to hold both the request and response, and a few helper functions. The code I created (modified from an earlier package created before AWS supported Go on Lambda) is hosted on Github.
In this version, the handler takes an
alexa.Request and returns an
At this point, we’ve done all we set out to do.
- Automate deployment of Go-based AWS Lambdas
- Create and return Alexa Skill Responses
- Handle a variety of Alexa Skill Request attributes (Locale and Attributes)
Hope this helps other folks come up the learning curve faster!