Tutorial: Develop Your First Alexa Skill With the ASK SDK for Node.js

The following tutorial walks you through developing your first skill with the Alexa Skills Kit (ASK) SDK v2 for Node.js. The example skill has the following flow:

User: Alexa, what's the weather?
Alexa: The weather is sunny.

You start with an empty skill project, add request handlers, and then create a skill package. You then create a corresponding Amazon Web Services (AWS) Lambda function by using the AWS Management Console, and then configure the skill by using the ASK developer console. You then run your skill by using the simulator in the developer console or by using an Alexa-enabled device.

Prerequisites

Before you follow this tutorial, you must have the following accounts and tools:

  • Amazon developer account – You need an Amazon developer account to create and configure Alexa skills. For details about how to create an account, see Create Your Amazon Developer Account.
  • AWS account – You need an AWS account to host your skill code on AWS Lambda. To create an AWS account, see AWS Free Tier.
  • Node Package Manager (NPM) – For details about how to install NPM, see Getting started in the NPM documentation.
  • ASK SDK v2 for Node.js – For details about installing the SDK, see Set up the ASK SDK v2 for Node.js. This tutorial requires one of the following installations:
    • the standard SDK distribution.
    • the support modules for the core SDK, if you want to customize your dependencies.

Steps to create a skill with the ASK SDK for Node.js

To create a skill, take the following steps.

  1. Create an empty skill project.
  2. Import dependencies.
  3. Add request handlers.
  4. Create the skill package.
  5. Create an AWS Lambda function.
  6. Create and configure your skill.
  7. Test your skill.

Step 1: Create an empty skill project

To create an empty skill project by using NPM, take the following steps.

To create an empty skill project

  1. On your computer, create a folder for your skill.
  2. Navigate to your skill folder by using the command line.
  3. Type npm init.
  4. Press enter for all the prompts.
    In your skill code folder, you now have a file called package.json.
  5. From your skill code folder, enter npm install --save ask-sdk.
    This command creates a node_modules folder.
  6. In your skill folder, create an empty file named index.js.

Step 2: Import dependencies

To indicate that your code relies on the ASK SDK for Node.js, you must import the SDK as a dependency.

To import dependencies

  • Paste the following code into your index.js file.

Copied to clipboard.

const Alexa = require('ask-sdk-core');

Copied to clipboard.

import {
  ErrorHandler,
  HandlerInput,
  RequestHandler,
  SkillBuilders,
} from 'ask-sdk-core';
import {
  Response,
  SessionEndedRequest,
} from 'ask-sdk-model';

Step 3: Add request handlers

Request handlers handle the different types of incoming requests to your skill. The following section provides code examples for request handlers that you can paste into your skill code.

LaunchRequest handler

When the user invokes your skill without a specific intent, Alexa sends your skill a LaunchRequest. The following code configures a handler that Alexa invokes when your skill receives a LaunchRequest.

To add a LaunchRequest handler

  • Paste the following code into your index.js file after the require line that you added previously.

Copied to clipboard.

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
  },
  handle(handlerInput) {
    const speechText = 'Welcome to your SDK weather skill. Ask me the weather!';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('Welcome to your SDK weather skill. Ask me the weather!', speechText)
      .getResponse();
  }
};

Copied to clipboard.

const LaunchRequestHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'LaunchRequest';        
  },
  handle(handlerInput : HandlerInput) : Response {
    const speechText = 'Welcome to your SDK weather skill. Ask me the weather!';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('Welcome to your SDK weather skill. Ask me the weather!', speechText)
      .getResponse();
  },
};

In the code, the canHandle() function returns true if the incoming request is a LaunchRequest. The handle() function generates and returns a response to Alexa.

AskWeatherIntent handler

The following code configures a handler that Alexa invokes when the user asks for the weather.

To add an AskWeatherIntent handler

  • Paste the following code after the previous handler in your index.js file.

Copied to clipboard.

const AskWeatherIntentHandler = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
      && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AskWeatherIntent';
  },
  handle(handlerInput) {
    const speechText = 'The weather today is sunny.';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('The weather today is sunny.', speechText)
      .getResponse();
  }
};

Copied to clipboard.

const AskWeatherIntentHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    const request = handlerInput.requestEnvelope.request;  
    return request.type === 'IntentRequest'
      && request.intent.name === 'AskWeatherIntent';
  },
  handle(handlerInput : HandlerInput) : Response {
    const speechText = 'The weather today is sunny.';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('The weather today is sunny.', speechText)
      .getResponse();
  },
};

In the code, the canHandle() function detects if the incoming request is an IntentRequest, and returns true if the intent name is AskWeatherIntent. The handle() function returns a response with Alexa speech that says the weather.

HelpIntent handler

The following code configures a handler that Alexa invokes when the skill receives the built-in intent AMAZON.HelpIntent. For details about how user utterances trigger built-in intents, see Standard Built-in Intents.

To add a HelpIntent handler

  • Paste the following code after the previous handler in your index.js file.

Copied to clipboard.

const HelpIntentHandler = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
      && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
  },
  handle(handlerInput) {
    const speechText = 'You can ask me the weather!';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('You can ask me the weather!', speechText)
      .getResponse();
  }
};

Copied to clipboard.

const HelpIntentHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    const request = handlerInput.requestEnvelope.request;    
    return request.type === 'IntentRequest'
      && request.intent.name === 'AMAZON.HelpIntent';
  },
  handle(handlerInput : HandlerInput) : Response {
    const speechText = 'You can ask me the weather!';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('You can ask me the weather!', speechText)
      .getResponse();
  },
};

Similar to the previous handler, the HelpIntent handler matches an IntentRequest with the expected intent name. The handler returns a response in which Alexa provides the suggestions you specify in the handler.

CancelAndStopIntent handler

You can use a single handler to respond to two different intents. The following example uses a single handler to respond to both the AMAZON.CancelIntent and the AMAZON.StopIntent built-in intents. The response to both intents is the same, so having a single handler reduces repetitive code.

To add a CancelAndStopIntent handler

  • Paste the following code after the previous handler in your index.js file.

Copied to clipboard.

const CancelAndStopIntentHandler = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
      && (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'
        || Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');
  },
  handle(handlerInput) {
    const speechText = 'Goodbye!';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('Goodbye!', speechText)
      .withShouldEndSession(true)
      .getResponse();
  }
};

Copied to clipboard.

const CancelAndStopIntentHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest'
      && (request.intent.name === 'AMAZON.CancelIntent'
         || request.intent.name === 'AMAZON.StopIntent');
  },
  handle(handlerInput : HandlerInput) : Response {
    const speechText = 'Goodbye!';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('Goodbye!', speechText)
      .withShouldEndSession(true)      
      .getResponse();
  },
};

SessionEndedRequest handler

Although your skill can't return a response after receiving a SessionEndedRequest, you can provide a handler that contains clean-up logic. The following example shows how to create a handler for a SessionEndedRequest.

To add a SessionEndedRequest handler

  • Paste the following code after the previous handler in your index.js file.

Copied to clipboard.

const SessionEndedRequestHandler = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
  },
  handle(handlerInput) {
    // Any clean-up logic goes here.
    return handlerInput.responseBuilder.getResponse();
  }
};

Copied to clipboard.

const SessionEndedRequestHandler : RequestHandler = {
  canHandle(handlerInput : HandlerInput) : boolean {
    const request = handlerInput.requestEnvelope.request;    
    return request.type === 'SessionEndedRequest';
  },
  handle(handlerInput : HandlerInput) : Response {
    console.log(`Session ended with reason: ${(handlerInput.requestEnvelope.request as SessionEndedRequest).reason}`);

    return handlerInput.responseBuilder.getResponse();
  },
};

Error handler

You can put your error-handling logic in an error handler. For example, you can add logic that handles unhandled requests, API service timeouts, and so on. The following code adds an error handler to your skill to ensure that your skill returns a meaningful message for all errors.

To add an error handler

  • Paste the following code after the previous handler in your index.js file.

Copied to clipboard.

const ErrorHandler = {
  canHandle() {
    return true;
  },
  handle(handlerInput, error) {
    console.log(`Error handled: ${error.message}`);

    return handlerInput.responseBuilder
      .speak('Sorry, I don\'t understand your command. Please say it again.')
      .reprompt('Sorry, I don\'t understand your command. Please say it again.')
      .getResponse();
  }
};

Copied to clipboard.

const ErrorHandler : ErrorHandler = {
  canHandle(handlerInput : HandlerInput, error : Error ) : boolean {
    return true;
  },
  handle(handlerInput : HandlerInput, error : Error) : Response {
    console.log(`Error handled: ${error.message}`);

    return handlerInput.responseBuilder
      .speak('Sorry, I don\'t understand your command. Please say it again.')
      .reprompt('Sorry, I don\'t understand your command. Please say it again.')
      .getResponse();
  }
};

Lambda handler

The Lambda handler is the entry point for your AWS Lambda function. The following code creates a Lambda handler function to route all inbound request to your skill.

To add a Lambda handler

  • Paste the following code after the previous handler in your index.js file.

Copied to clipboard.

let skill;

exports.handler = async function (event, context) {
  console.log(`REQUEST++++${JSON.stringify(event)}`);
  if (!skill) {
    skill = Alexa.SkillBuilders.custom()
      .addRequestHandlers(
        LaunchRequestHandler,
        AskWeatherIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        SessionEndedRequestHandler,
      )
      .addErrorHandlers(ErrorHandler)
      .create();
  }

  const response = await skill.invoke(event, context);
  console.log(`RESPONSE++++${JSON.stringify(response)}`);

  return response;
};

Copied to clipboard.

let skill;

exports.handler = async (event, context) => {
  console.log(`REQUEST++++${JSON.stringify(event)}`);
  if (!skill) {
    skill = SkillBuilders.custom()
      .addRequestHandlers(
        LaunchRequestHandler,
        AskWeatherIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        SessionEndedRequestHandler,
      )
      .addErrorHandlers(ErrorHandler)
      .create();
  }

  const response = await skill.invoke(event, context);
  console.log(`RESPONSE++++${JSON.stringify(response)}`);

  return response;
};

The Lambda handler function creates an SDK Skill instance by using the SkillBuilders.custom() builder function. The addRequestHandlers() builder function registers the request handlers that you created in the previous steps. The code exports the function as the Lambda handler function.

Alternatively, ASK SDK v2 for Node.js provides a lambda builder function that you can use to construct the Lambda handler function that invokes the Skill instance and returns the response. The following example shows how to use the lambda builder function.

Copied to clipboard.

exports.handler = Alexa.SkillBuilders.custom()
  .addRequestHandlers(
    LaunchRequestHandler,
    AskWeatherIntentHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler)
  .addErrorHandlers(ErrorHandler)
  .lambda();

Copied to clipboard.

exports.handler = SkillBuilders.custom()
  .addRequestHandlers(
    LaunchRequestHandler,
    AskWeatherIntentHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler,
  )
  .addErrorHandlers(ErrorHandler)
  .lambda();

Step 4: Create the skill package

With the skill code complete, you can create the skill package that you upload to AWS Lambda in a subsequent step.

To create a skill package

  • Create a zip file that contains the contents of your skill code folder. In other words, the zip file contains index.js, package.json, and the node_modules folder.

Step 5: Create an AWS Lambda function

In the previous steps, you created the code for a Lambda function. You must also create the corresponding Lambda function in the AWS console. You then upload your skill code to the Lambda function.

To create an AWS Lambda function

  1. Log in to the AWS Lambda console.
  2. From the Region drop-down list in the upper-right corner of the console, select one of the allowed AWS regions for Alexa skills, such as US East (N. Virginia). For a list of allowed regions for Alexa skills, see Select the optimal region for your AWS Lambda function.
  3. If you have no Lambda functions yet, click Get Started Now. Otherwise, click Create function.
  4. Select Author from scratch.
  5. For Function name, enter a name for your Lambda function.
  6. For Runtime, select Node.js 10.x.
  7. For Permissions, expand Change default execution role, and then select Create a new role from AWS policy templates.
  8. For Role name, enter a name for the role.
  9. From the Policy templates list, select Simple Microservice permissions.
  10. At the bottom of the page, click Create function.
  11. Click Function overview, and then click Add trigger.
  12. For Trigger configuration, select Alexa Skills Kit.
  13. For Skill ID verification, select Disable.
  14. Click Add.
  15. In the middle of the page, click the Code tab.
  16. On the right, click Upload from, and then select .zip file.
  17. Upload the zip file you produced in Step 4: Create the skill package.
  18. At the top right of your Lambda function page, click Copy ARN, and then paste the ARN somewhere you can access it later.
    You need the ARN when you configure your skill in the ASK developer console in the next step.

Step 6: Create and configure your skill

Now that your skill code is in AWS Lambda, you can create and configure your skill in the ASK developer console.

To create and configure an Alexa skill

  1. Sign in to the ASK developer console.
  2. Click Create Skill.
  3. Enter a name for your skill.
  4. For Choose a model to add to your skill, select Custom.
  5. For Choose a method to host your skill's backend resources, select Provision your own.
  6. At the top, click Create skill.
  7. For Choose a template to add to your skill, select Start from Scratch.
  8. At the top right, click Choose.
  9. On the left, click Invocation.
  10. For Skill Invocation Name, enter my sdk weather skill, and then click Save Model.
    To launch your skill, you will say, "Alexa, open my sdk weather skill."
  11. On the left, expand Interaction Model, click Intents, and then click Add Intent.
  12. Under Create custom intent, enter AskWeatherIntent, and then click Create custom intent.
  13. Under Sample Utterances, add some sample utterances that a user might say to ask for the weather, and click + or press enter after each entry.
    You can add the following sample utterances; feel free to add others.
    what is the weather
    what's the weather
    what is it like out
    what's it like out
    
  14. At the top, click Save Model.
  15. At the top, click Build Model.
  16. On the left, expand Assets, and then click Endpoint.
  17. Replace the Default Region with the AWS Lambda ARN you copied in Step 5: Create an AWS Lambda function.
  18. At the top, click Save Endpoints.

Step 7: Test your skill

You can test your skill by using the Alexa Simulator in the developer console or by using an Alexa-enabled device such as an Echo device. For details about testing skills, see Test Your Skill.

To test your skill in the developer console

  1. At the top of the developer console, click the Test tab.
  2. On the left, at the top, for Test is disabled for this skill, select Development.
  3. In the Alexa Simulator, click in the Type or click and hold the mic field, type open my sdk weather skill, and then press enter.
  4. Enter or say "Alexa, what's the weather?"
    Alexa should respond, "The weather is sunny."

You can also test your skill by using an Alexa-enabled device, such as an Echo device, that is registered to the same Amazon account as the account in which you created your skill.

To test your skill by using an Alexa-enabled device

  1. Say to your Alexa-enabled device, "Alexa, open my sdk weather skill."
  2. After Alexa opens your skill, ask, "Alexa, what's the weather?"
    If Alexa can't find your skill, make sure that your Alexa-enabled device is registered to the same Amazon account that you used to create the skill.