« Back
in serverless lambda AWS Node.js Alexa read.
Alexa Custom Skill Gotchas with Serverless Framework

Alexa Custom Skill Gotchas with Serverless Framework.

If you're looking to build a custom skill for Alexa using Serverless because you don't want the hassle of a manual deployment or the other setup fluff you've come to the right place. This will be a list of gotchas that I ran into when working with the Alexa SDK for Node.JS.

There are plenty of articles out there on demoing the stuff and what all the different parts needed are and this will not be that. It is assumed that you have already understood utterances, the incantation words and intents. So hopefully this will save someone lots of time when they decide to deviate even a little bit outside the canned examples out there.

Even though this is a list of problems, I still think that the Echo and Alexa are very cool and is building a better voice powered future. I got it up and running in a test environment in less than a day. The examples out there are way easier to understand than the documentation itself so don't let that discourage you from trying it out.

1. You Named Your Lambda Function

Looks like you decided to call it something else instead of handler() like all the other examples out there. You might have more than one function in the serverless.yml so you named this one say myFunction.

Problems:

  • can't save session state using:
this.handler.state  
  • can't save session values using:
this.attributes['myattr'] = someValue  
  • can't get Alexa to tell or ask another question using:
this.emit(':tell', 'Alexa say what')  
  • can't get the custom slot value with:
this.event.request.intent.slots.CustomSlot.value  

E.g. Custom Function Name
serverless.yml

functions:  
  firstFunction:
    handler: handler.myFunction
    timeout: 30
    events:
      - alexaSkill

The Fix

I think the code is pretty self explanatory but basically the members you need to access are in completely different scope. See below code for the correct scope.

handler.js

const Alexa = require('alexa-sdk');  
const states = {  
  START: '_START',
  ASK_LAST_NAME: '_ASK_LAST_NAME'
};

var $event = null;  
var $alexa = null;

module.exports.myFunction= ( event, context, callback) => {  
  var alexa = Alexa.handler(event, context);
  //alexa.appId = 'MY APP ID';
  $event = event;
  $alexa = alexa;

  alexa.registerHandlers(myHandler);
  alexa.execute();
}

const myHandler = Alexa.CreateStateHandler(states.START, {  
  'MyIntent': () => {

    // This won't work
    // this.attributes['firstName'] = this.event.request.intent.slots.FirstName.value;

    // This works
    $event.session.attributes['firstName'] = $event.request.intent.slots.FirstName.value;


    // This won't work
    // this.handler.state = states.ASK_LAST_NAME;

    // This works
    $alexa.state = states.ASK_LAST_NAME;


    // This won't work
    // this.emit(':ask', 'What is your last name?');

    // This works
    $alexa.emit(':ask', 'What is your last name?');

  }
});

2. Lambda Needs Access to DynamoDB

This is more of a Serverless framework thing. If we need to specify a specific DynamoDB resource, we need to declare schema up front. The Alexa SDK will manage states and do all the CRUD work for you including making the tables. So instead of declaring a resource we give the Lambda function permission to use DynamoDB instead.

E.g.
serverless.yml

provider:  
  name: aws
  runtime: nodejs4.3
  stage: dev
  region: us-east-1
  iamRoleStatements: # permissions for all of your functions can be set here
    - Effect: Allow
      Action: # Gives permission to DynamoDB tables in a specific region
        - dynamodb:DescribeTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
        - dynamodb:CreateTable
      Resource: "arn:aws:dynamodb:us-east-1:*:*"

Just a bit of a side note: If your lambda function writes to RDS, make sure that the instance is in the same VPC and the function has InBound access to the instance on the roles.

3. Reducing the Size of node_modules by Exclusion

I tried reducing the size of my deployments by removing the testing frameworks in serverless.yml and only include the Alexa SDK and actual dependencies.

# Warning!!! Does not work
package:  
  include:
    - handler.js
  exclude:
    - node_modules/mocha/**
    - '!node_modules/alexa-sdk/**'

This didn't work. The error I got on CloudWatch logs was that the Alexa.handler() function was undefined. If someone knows whats only needed please let me know in the comments.

4. Lambda Function Didn't Update

I went to the AWS console to download my function to ensure that my deployed function was reflective of my code. Sometimes it wasn't. Most of the time it was. Modified your node_modules exclusions and inclusions list? It may not have updated using:

serverless deploy function --function myFunction  

What did fix it though was if I just straight up did:

serverless deploy  

I believe serverless deploy uses cloud formation to update the stack so it may take a little longer. This was the most reliable method for me. Even then, some things like the timeout decided not to update when I checked the console.

Still, it's good for a quick play and saves a lot of manual hassles.

These are just a quick list of some problems I ran into. By no means is it production code but I hope it saves some WTF moments especially with all the examples out there using the default handler function name.

comments powered by Disqus