alexa-router

Easily build custom skills for Alexa

Usage no npm install needed!

<script type="module">
  import alexaRouter from 'https://cdn.skypack.dev/alexa-router';
</script>

README

alexa-router

Build Status Code Climate Test Coverage Version Downloads

Why

alexa-router makes it easy for you to build custom Alexa skills with complex request/response flows.

Install

$ npm install -S alexa-router

Usage

alexa-router is available via an instance of the Router. Make sure you begin by initializing the Router.

let Alexa = require('alexa-router')
let alexa = new Alexa.Router({
  appId: 'my-app-id'
})

Once you initialize the router, you can either configure actions or dispatch a HTTP request to be routed to the actions you have configured.

Router

API

new Alexa.Router(config)

config

Required
Type: Object

config.appId
Required
Type: String[]

Your application ID or an array with many

config.routeIntentOnly
Optional
Type: Boolean
Default: true

Try to route IntentRequest only

config.verifySignature
Optional
Type: Boolean
Default: true

Verifies the incoming request against a valid Amazon signature to prevent request forgery. Amazon requires verification as part of the skill submission process

config.verifyTimestamp
Optional
Type: Boolean
Default: true

Verifies if the incoming request have a valid timestamp to prevent replay attacks

config.verifyAppId
Optional
Type: Boolean
Default: true

Verifies if the incoming request have a valid application ID to prevent replay attacks from other applications

Examples

let alexa = new Alexa.Router({
  appId: 'my-id',
  verifySignature: false
})

alexa.action

Routes are defined via the action method

API

alexa.action(name, config)

name

Required
Type: String

The action name. You can reference this action by its name when defining complex action flows.

config

Required
Type: Object

config.handler(request[, params])

Required
Type: Function

The handler receives a decorated HTTP request and optionally receives params if they were configured to be passed by a previous action.

request

Type: Object

The decorated [Alexa Request Body](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/alexa-skills-kit-interface-reference#Request Format) with additional methods.

request.next()
Returns: Array
An array of Next Action Objects set by the previous response in the session

request.response()
Returns: Object
A decorated Alexa Response Object

// Instantiate a new Response object
let response = request.response()

response.session(key, value)
Param: key String
Param: value String|Object Sets, patches, or retrieves the session's attributes

response.speech(text)
Param: text String
Convenience method to set speech with raw text or SSML

response.reprompt(text)
Param: text String
Convenience method to set reprompt with raw text or SSML

response.card(card)
Param: card Object a valid [Alexa Card Object](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/alexa-skills-kit-interface-reference#Response Format)
Convenience method to set the response card

response.endSession(shouldEndSession)
Param: shouldEndSession Boolean
A convenience to set the response shouldEndSession property

response.clearSession()
Clears the current session

response.next(config)
Param: config Object|Array
If you pass an object it will be merged with any previous Next Action Objects that were passed

      The Next Action Object
      type
      Required
      Type: String

      One of 'intent', 'launch', 'sessionEnded', or 'unexpected'

      action
      Required
      Type: String

      The name of the action that should be called if this route is activated

      intent
      Required if type === 'intent'
      Type: String

      The custom or built-in intent that this action should be associated with. e.g. 'AMAZON.YesIntent'

      params
      Optional
      Type: Any

      Any data you'd like to pass to follow request if this route is activated

params

Type: Object

Params set by a previous action

config.global

Optional
Type: Object

Actions with the global key are accessible at any point in the routing flow (like a catch-all). These actions can be used to kick-off a new flow, interrupt an existing flow, etc. An action to help the user know what commands are available or cancel the request are two examples for where you might use a global action.

config.global.type
Required
Type: String

One of 'intent', 'launch', 'sessionEnded', or 'unexpected'

config.global.intent
Required if type === 'intent'
Type: String

The custom or built-in intent that this action should be associated with. e.g. 'AMAZON.YesIntent'

Examples

A simple action that can be activated by an incoming intent

alexa.action('global-hello', {
  handler: request => {...},
  global: {
    type: 'intent', // Could also be launch, sessionEnded or unexpected
    intent: 'AMAZON.YesIntent'
  }
})

You can also chain requests by responding with a list of possible actions that could be next in the interaction flow

alexa.action('event-create', {
  handler: request => {
    let response = request.response()
    response.speech('What\'s the name of your event?')

    // You can define the next actions by passing an array of actions that can come next
    response.next([
      {
        type: 'intent',
        intent: 'EventName', // Custom intent
        action: 'event-create-name'
        params: { createdAt: new Date() } // Params will be passed to the `event-create-name` handler
      },
      {
        type: 'intent',
        intent: 'AMAZON.CancelIntent', // Built-in intent
        action: 'event-cancel'
      }
    ])

    // You can also pass an individual object and it will be merged with the previous ones
    response.next({
      type: 'unexpected',
      action: 'event-unexpected'
    })

    return response
  },

  global: {
    type: 'intent',
    intent: 'EventCreate' // Custom intent
  }
})

// This action does not have the global attribute so it can only be accessed if passed
// as a `next` action
alexa.action('event-create-name', {
  handler: (request, params) => {...}
})

alexa.dispatch

The dispatch method takes a HTTP request and routes it to the appropriate action

API

alexa.dispatch(requestBody, headers)

requestBody

Required
Type: 'Object'

The HTTP request body

Headers

Required
Type: 'Object'

The headers present in the original incoming request

Understanding the routing mechanism

  1. Check if the incoming request was configured with next actions
  2. If next actions are present, try to resolve the next action
  3. If no action was resolved, check for an unexpected type next option
  4. If no next actions are present in the request's session, try to match a global action
  5. If no global action was found try to find an unexpected global action
  6. If no unexpected global action then throw RoutingFailed

HTTP handling

alexa-router is HTTP server agnostic. This means that you can use it with any Node.js library that can parse and reply JSON. An example using Express:

let express = require('express')
let bodyParser = require('body-parser')
let Alexa = require('alexa-router')

let app = express()
let alexa = new Alexa.Router({
  appId: 'my-app-id'
})

// Do all your routing configs
alexa.action('my-action', ...)

// Configure a route for passing JSON to alexa-router
app.post('/alexa/incoming', bodyParser.json(), (req, res) => {
  alexa.dispatch(req.body, req.headers)
  .then(result => res.json(result))
  .catch(err => {
    res.send('Somthing bad happened...').status(500)
  })
})

To-do

  • Add plugin support
  • Add more testing cases
  • Build plugins for Express, Hapi, Restify (...)

Testing

git clone https://github.com/estate/alexa-router && cd alexa-router
npm install && npm test