We recently released version two of the Alexa Skills Kit (ASK) Software Development Kit (SDK) for Node.js, which introduced a fresh approach to handling requests and responses (among many other enhancements to the SDK). In our new code deep dive series, we’ll show you the ins and outs of the new SDK by providing a step-by-step code overview of the new features. We will also build a new skill from scratch, bit by bit, as we progress through the series.
Throughout this series, we will create a listening retention memory game skill that will help customers improve their listening skills in a fun way. Alexa will read a short passage and then ask customers one or more questions related to that passage. Alexa will generate the passages at random. If customer gets a question right, they get points! Eventually they could earn badges and other engaging experiences along the way.
Here's a sample conversation we want to build:
Customer: Alexa, open Memory Challenge.
Alexa: Welcome to Memory Challenge. I will read you a short passage, and then ask you question based on that. Are you ready?
Customer: Yes.
Alexa: Ok. Here we go. John loves sports. His favorite sports in the Olympics are ice skating and skiing for the Winter Olympics, and basketball and volleyball for the Summer Olympics. What are John's favorite games for the Winter Olympics?
Customer: Ice skating and skiing.
Alexa: Bingo! That's right. Do you want to continue?
Customer: Yes.
As we build the skill, we will add features to it gradually, testing each one along the way. For the first code deep dive, we will build the scaffold for our skill using the new request handling features in version two of the ASK SDK for Node.js. You can learn about the request handling features here and then read the rest of this blog post to see those features and concepts implemented as we write the code from scratch for our AWS Lambda function, which will serve as the backend for requests coming to our skill.
We have one main objective for this code deep dive–when customers start the skill by saying, "Alexa, open memory challenge," we want the skill to respond with a welcome greeting like the one we included in the conversation above (“Welcome to memory challenge…”).
This deep dive will teach you how to build request handlers for incoming requests using canHandle() and handle() methods. You’ll also learn how to register the request handlers. Let’s start building.
Since we’ll be using version two of the ASK SDK for Node.js to write this Lambda function for our skill, the first thing we’ll do is use JavaScript’s require function to include the core ASK SDK module in our Lambda function. The Core Alexa SDK module provides us everything we need to get our skill started. As we expand the functionality of our skill in future deep dives, we will look into other flavors of the ASK SDK for Node.js.
When a customer starts your skill by saying, “open memory challenge,” your skill receives a request of type “LaunchRequest.”
To handle this request, wecreate a new object called LaunchRequestHandler. You can name it whatever you want but give it a name that’s related to the kind of requests it’s capable of handling for easy recall. For example, in our case, we name it LaunchRequestHandler, since it would be doing just that–handling launch requests.
This is probably the most significant change compared to the previous version of the ASK SDK. In the new SDK, the request routing is based on a “can you handle this?” concept, which means that every handler lays out the specific condition/s that it is capable of handling. This is done within the canHandle() function defined inside the handler. This function is required in every handler. As we will see in later installments of this series, this lets you write a single handler for multiple intents, or handlers that check for other values, like session attributes.
The second piece to this is the handle() function, which includes the execution logic for the handler. If the condition/s set in the canHandle() function return true, the code included in the handle() function is executed. If not, the search for the appropriate handler continues.
When a customer interacts with an Alexa skill, your service (Lambda function) receives a POST request containing a JSON body. This request body contains the parameters we would need to figure out what the customer is asking for and is made available by the SDK to all request handlers through a HandlerInput object.
Here’s an example of what the HandlerInput object may look like for a LaunchRequest:
{
"requestEnvelope": {
"version": "1.0",
"session": {
. . .
. . .
},
"context": {
. . .
. . .
},
"request": {
"type": "LaunchRequest",
"requestId": "amzn1.echo-api.request.403abfd8-625c-4717-94e3-76c20e449c7a",
"timestamp": "2018-05-24T02:35:25Z",
"locale": "en-US",
"shouldLinkResultBeReturned": false
}
},
"context": {
. . .
. . .
},
"attributesManager": {},
"responseBuilder": {}
}
What we are interested in is the request object inside requestEnvelope in HandlerInput object. We grab that and assign it to a variable named request. We will use this variable to set our “can you handle this” conditions for our LaunchRequestHandler in the next step.
Next, we set the conditions. We want this handler to execute if the request type is a LaunchRequest. Remember for the handler to execute the logic inside the handle() function, the canHandle() function must return true. We return true if the request type is a ‘LaunchRequest’ (you can review the sample request JSON above to see that the request type for that example is in fact LaunchRequest).
Let’s add the logic for our request handler inside the handle() function. If the request is a LaunchRequest, that means that the customer has asked the skill to be launched, without a specific request to a functionality within the skill, so we should probably welcome the customer with a welcome message and tell them briefly what the skill can do.
We generate a speech response by using the speak() method of responseBuilder. The responseBuilder is a helper function provided by the ASK SDK and is used to generate all kinds of responses back to the customer (speech, cards, images, plain text and rich text for devices with display). We will dive deep into ResponseBuilder in the future code walkthroughs, but for now we will use the .speak() method to generate the welcome message speech and then use the .getResponse() method to package that into a JSON response and send it back to the customer.
Let's refactor that a bit to improve readability:
We are almost done with our basic version of the skill. All we need to do now is create an instance of the skill. There are a few different skill builders you can use depending on the features your skill needs. Because our skill is fairly straightforward, we will use CustomSkillBuilder. You can read more about other skill builders available here.
Since we are using AWS Lambda to host our skill code, we need to create handler object (exports.handler), which will serve as the entry point that AWS Lambda will use to execute our function code.
To ensure that this AWS Lambda entry point is aware of the names of all our request handlers, we register them by using skill builder’s built-in addRequestHandlers() method. Since we only have one request handler for now, LaunchRequestHandler, we add that. You will notice in future code deep dives as we add more request handlers that the order in which you type them inside the addRequestHandlers matters. It starts with the first handler and goes down the list asking the question “can you handle this” until it gets to a request handler that returns true.
Finally, we call the .lambda(), and pass it the handler object:
Now we’ve created a basic scaffold for our skill. If you would like to build this skill with us throughout the series, follow the steps below to kick-start your skill:
In the next code deep dive, we will add some more request handlers to our skill to handle customer requests like, “Alexa, ask memory challenge to start a nerdy memory challenge.” We will be using slots to accept customer input and cater our responses accordingly.