Writing Alexa Skills in TypeScript using Skills SDK V2

In April, Amazon released a new version of their Alexa Skills Kit SDK for nodejs, which included the package being renamed to ask-sdk. The new version now includes TypeScript definitions which is great news for us TS devs, however they don’t provide (at the time of writing this) any examples or boilerplate code for TypeScript.

So I will give a basic example to get you up and running. My approach was to try and port across a hello world example skill from plain JavaScript. It’s really just a process of looking through the TypeScript definitions for the SDK to find the correct interfaces to implement.

Request Handlers

A regular request handler implements the RequestHandler interface, you will use this for all of your custom handlers as well as the built in Intents such as LaunchRequestAMAZON.CancelIntentAMAZON.StopIntent etc.

The only exception to this is the Error Handler which has a different signature for the handle method, this needs to implement ErrorHandler, this also needs to be registered separately from the other handlers.

The type for the input parameter of the two methods is HandlerInput.

import { HandlerInput, RequestHandler } from 'ask-sdk';
import { Response } from 'ask-sdk-model';

export class AmazonStopIntentHandler implements RequestHandler {
  canHandle(handlerInput: HandlerInput): boolean {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest' && request.intent.name === 'AMAZON.StopIntent';
  }

  handle(handlerInput: HandlerInput): Response {
    const responseBuilder = handlerInput.responseBuilder;

    const say = 'welcome from typescript';

    return responseBuilder.speak(say)
    .getResponse();
  }
}

Entry Point

This is where I got stuck for a little bit, it’s important to remember that we are returning a LambdaHandler, not a skill builder. The .lambda() method serializes the object to JSON ready for the Alexa Skill to read.

Your exported property (“handler” in this case) must match your configured Handler in your Lambda configuration, the default handler is index.handler.
Lambda Function Entry Point Configuration

When the Alexa Skill calls the Lambda function, it will look for the exported property named handler in index.js. If you have configured your TypeScript to transpile to a filename other than index.js, you will need to update your Lambda configuration to reflect the new name.

I have created request handlers in separate files for all the default Alexa examples as well as one custom request handler named HelloHandler. I then call the skill builder and pass the collection of initialized handlers into the addRequestHandlers method. The error handler is added in a separate call, since the error handler is of a different type to the others. This is one key difference to the regular JS examples, rather than defining an object and referencing it in the skill builder, we are creating our handlers as classes, which means we need to instantiate them.

import { HandlerInput, RequestHandler, SkillBuilders, BaseSkillBuilder } from 'ask-sdk';
import { LambdaHandler } from 'ask-sdk-core/dist/skill/factory/BaseSkillFactory';

import { LaunchRequestHandler } from './handlers/LaunchRequestHandler';
import { AmazonCancelIntentHandler } from './handlers/AMAZON_CancelIntent_Handler';
import { AmazonStopIntentHandler } from './handlers/AMAZON_StopIntent_Handler';
import { HelloHandler } from './handlers/HelloHandler';
import { SessionEndedHandler } from './handlers/SessionEndedHandler';
import { CustomErrorHandler } from './handlers/CustomErrorHandler';

function buildLambdaSkill(): LambdaHandler {
  return SkillBuilders.standard()
    .addRequestHandlers(
      new AmazonCancelIntentHandler,
      new AmazonStopIntentHandler,
      new HelloHandler(),
      new LaunchRequestHandler(),
      new SessionEndedHandler()
    )
    .addErrorHandlers(new CustomErrorHandler())
    .lambda();
}

// Lambda handler - entry point for skill
export let handler = buildLambdaSkill();

Project source can be found on GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *