Table of Contents
- Stable Release
- Introduction
- Features
- Examples
- API
- Request Handlers
- Execute Code On Every Request
- Schema and Utterances
- Cards
- Custom Directives
- Dialog
- Error Handling
- Echo Show Support
- Asynchronous Handlers Example
- License
alexa-app
A Node module to simplify the development of Alexa skills (applications.)
Stable Release
You're reading the documentation for the stable release of alexa-app, 4.2.3. Please see CHANGELOG and make sure to read UPGRADING when upgrading from a previous version.
Introduction
This module parses HTTP JSON requests from the Alexa platform and builds the JSON response that consumed by an Alexa-compatible device, such as the Echo.
It provides a DSL for defining intents, convenience methods to more easily build the response, handle session objects, and add cards.
The intent schema definition and sample utterances are included in your application's definition, making it very simple to generate hundreds (or thousands!) of sample utterances with a few lines.
This module provides a way to host a standalone web service for an Alexa skill. If you're looking for a full-fledged application server or the ability to host multiple skills, check out alexa-app-server.
Features
- simplified handling of requests and generating responses
- support for asynchronous handlers
- easy connection into AWS Lambda or Node.js Express, etc.
- auto-generation of intent schema and sample utterances
- support for session data
- comprehensive test suite
- TypeScript type definitions for type validation, IDE autocompletion, etc
Examples
AWS Lambda
Amazon skills that use alexa-app have a built-in handler
method to handle calls from AWS Lambda.
You need to make sure that the Handler is set to index.handler
, which is the default value.
var alexa = ;var app = "sample"; app; // connect the alexa-app to AWS Lambdaexportshandler = app;
For backwards compatibility, or if you wish to change the Handler mapping to something other than index.handler, you can use the lambda() function.
A full lambda example is available here.
Express
var express = ;var alexa = ;var express_app = ; var app = "sample"; app; // setup the alexa app and attach it to express before anything elseapp; // now POST calls to /sample in express will be handled by the app.request() function// GET calls will not be handled // from here on, you can setup any other express routes or middleware as normal
The express function accepts the following parameters.
expressApp
the express app instance to attach torouter
the express router instance to attach toendpoint
the path to attach the express app or router to (e.g., passing'mine'
attaches to/mine
)checkCert
when true, applies Alexa certificate checking (default: true)debug
when true, sets up the route to handle GET requests (default: false)preRequest
function to execute before every POSTpostRequest
function to execute after every POST
Either expressApp
or router
is required.
A full express example is available here.
Heroku Quickstart
Want to get started quickly with alexa-app and Heroku? Simply click the button below!
API
Skills define handlers for launch, intent, and session end, just like normal Alexa development. The alexa-app module provides a layer around this functionality that simplifies the interaction. Each handler gets passed a request and response object, which are custom for this module.
request
// return the type of request received (LaunchRequest, IntentRequest, SessionEndedRequest)String requesttype // return the value passed in for a given slot nameString request // return the Slot objectSlot requestslots"slotName" // return the intent's confirmationStatusString requestconfirmationStatus // check if the intent is confirmedBoolean request // return the Dialog objectDialog request // check if you can use session (read or write)Boolean request // return the session objectSession request // return the router objectRouter request // return the request contextrequestcontext // the raw request JSON objectrequestdata
response
The response JSON object is automatically built for you. All you need to do is tell it what you want to output.
// tell Alexa to say something; multiple calls to say() will be appended to each other// all text output is treated as SSMLresponse // empty the response textresponseclear // tell Alexa to re-prompt the user for a response, if it didn't hear anything validresponse // return a card to the user's Alexa app// for Object definition @see https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/alexa-skills-kit-interface-reference#card-object// skill supports card(String title, String content) for backwards compat of type "Simple"response // return a card instructing the user how to link their account to the skill// this internally sets the card responseresponse // play audio stream (send AudioPlayer.Play directive) @see https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/custom-audioplayer-interface-reference#play-directive// skill supports stream(String url, String token, String expectedPreviousToken, Integer offsetInMilliseconds)response // stop playing audio stream (send AudioPlayer.Stop directive)response // clear audio player queue (send AudioPlayer.ClearQueue directive)// clearBehavior is "CLEAR_ALL" by defaultresponse // tell Alexa whether the user's session is over; sessions end by default// pass null or undefined to leave shouldEndSession undefined in the response, to satisfy newer API's// you can optionally pass a reprompt messageresponse // send the response to the Alexa device (success) immediately// this returns a promise that you must return to continue the// promise chain. Calling this is optional in most cases as it// will be called automatically when the handler promise chain// resolves, but you can call it and return its value in the// chain to send the response immediately. You can also use it// to send a response from `post` after failure.async response // trigger a response failure// the internal promise containing the response will be rejected, and should be handled by the calling environment// instead of the Alexa response being returned, the failure message will be passed// similar to `response.send()`, you must return the value returned from this call to continue the promise chain// this is equivalent to calling `throw message` in handlers// *NOTE:* this does not generate a response compatible with Alexa, so when calling it explicitly you may want to handle the response with `.error` or `.post`async response // calls to response can be chained togetherreturn response
Building SSML Responses
Use ssml-builder to build SSML responses.
Example using basic SSML tags:
var Speech = ; var speech = ; // change 'true' to 'false' if you want to include the surrounding <speak/> tagvar speechOutput = speech;response;
Example using Amazon SSML specific tags:
var AmazonSpeech = ; var speech = ; var speechOutput = speech;response;
Example using multiple reprompts. The reprompts are spoken to the user if they do not respond to the main prompt or say something that does not map to a defined intent:
response ;
session
// check if you can use session (read or write)Boolean request // get the session objectvar session = request // set a session variable// by defailt, Alexa only persists session variables to the next request// the alexa-app module makes session variables persist across multiple requests// Note that you *must* use `.set` or `.clear` to update// session properties. Updating properties of `attributeValue`// that are objects will not persist until `.set` is calledsession // return the value of a session variableString session // session details, as passed by Amazon in the request// for Object definition @see https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/alexa-skills-kit-interface-reference#session-objectsessiondetails = ...
router
// get router object// every method of the router returns a promise, so you can chain them or just return it back to alexa-appvar router = request // route request to 'MySuperIntent' intent handlerPromise router // route request to Launch handlerPromise router // route request to session ended handlerPromise router // route request to audio player handlerPromise router // route request to playback controller handlerPromise router // route request to display element selected handlerPromise router // route request to custom handlerPromise router
slot
// get the slot objectvar slot = requestslots"slotName" // return the slot's nameString slotname // return the slot's valueString slotvalue // return the slot's confirmationStatusString slotconfirmationStatus // return the slot's resolutionsSlotResolution slotresolutions // check if the slot is confirmedBoolean slot // return the n-th resolutionSlotResolution slot
slotResolution
// get the resolution status codeString resolutionstatus // get the list of resolution valuesResolutionValue resolutionvalues // check if the resolution is matchedBoolean resolution // Get the first resolution valueResolutionValue resolution
resolutionValue
// get the value nameString resolutionValuename // get the value idString resolutionValueid
Request Handlers
Your app can define a single handler for the Launch
event and the SessionEnded
event, and multiple intent handlers.
For switching intents, redirecting from one handler to other and other routing tasks you can use router.
LaunchRequest
app;
IntentRequest
Define the handler for multiple intents using multiple calls to intent()
.
Additional Intent configuration schema like slots and sample utterances can also be passed to intent()
, which is detailed below.
Intent handlers that don't return an immediate response (because they do some asynchronous operation) must return a Promise. The response will be sent when the promise is resolved and fail when the promise is rejected.
See example further below.
app; app;
AMAZON Specific Intents
Amazon has specific intents that have to do with basic functionality of your skill that you must add. Some examples of this are AMAZON.HelpIntent
, AMAZON.StopIntent
, and AMAZON.CancelIntent
. Here are examples of how you would specify these types of intents.
app; app; app;
You do not need to pass any utterances or slots into these intents. Also when specifying the name of the intent just use the exact name Amazon provides.
Display Element Selected
Define the handler for when a user selects an element displayed on alexa touch enabled device. For instance the Echo Show.
app
SessionEndRequest
app;
AudioPlayer Event Request
Define the handler for multiple events using multiple calls to audioPlayer()
. You can define only one handler per event. Event handlers that don't return an immediate response (because they do some asynchronous operation) must return a Promise.
You can define handlers for the following events:
- PlaybackStarted
- PlaybackFinished
- PlaybackStopped
- PlaybackNearlyFinished
- PlaybackFailed
Please note:
PlaybackStarted
andPlaybackFinished
accept onlyStop
orClearQueue
directive in response.PlaybackStopped
does not accept any response.PlaybackNearlyFinished
andPlaybackFailed
accept any AudioPlayer directive in response.
Read more about AudioPlayer request types in AudioPlayer Interface Doc.
The following example will return play
directive with a next audio on AudioPlayer.PlaybackNearlyFinished
request.
app;
See an example of asynchronous response below.
app;
PlaybackController Event Request
PlaybackController events are sent to your skill when the user interacts with player controls on a device. Define multiple handlers for various events by making multiple calls to playbackController
with each event type.
You can define handlers for the following events:
- PlayCommandIssued
- PauseCommandIssued
- NextCommandIssued
- PreviousCommandIssued
Read more about PlaybackController requests in the PlaybackController Interface Reference.
The following example will send a play directive to the device when a user presses the "next" button.
app;
Note that some device interactions don't always produce PlaybackController events. See the PlaybackController Interface Introduction for more details.
Other Event Request
Handle any new requests that don't have an explicit handler type available (such as new or pre-release features) using the general on()
and passing the event type.
The following example will handle an imaginary request of type DeviceEngine.InputHandler
as if it were added to the Alexa API.
app;
Note that the raw request json is sent as the 3rd parameter to make sure the handler function has access to all data in the case that the request format differs from other handler types.
Execute Code On Every Request
In addition to specific event handlers, you can define functions that will run on every request.
pre()
Executed before any event handlers. This is useful to setup new sessions, validate the applicationId
, or do any other kind of validations.
You can perform asynchronous functionality in pre
by returning a Promise.
app { if requestapplicationId != "amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe" // fail ungracefully throw "Invalid applicationId"; // `return response.fail("Invalid applicationId")` will also work }; // Asynchronousapp { return db;};
Note that the post()
method still gets called, even if the pre()
function calls send()
or fail()
. The post method can always override anything done before it.
post()
The last thing executed for every request. It is even called if there is an exception or if a response has already been sent. The post()
function can change anything about the response. It can even turn a return response.fail()
into a return respond.send()
with entirely new content. If post()
is called after an exception is thrown, the exception itself will be the 4th argument.
You can perform asynchronous functionality in pre
by returning a Promise similar to pre
or any of the handlers.
app { if exception // always turn an exception into a successful response return responseclear; };
Schema and Utterances
The alexa-app module makes it easy to define your intent schema and generate many sample utterances. Optionally pass your schema definition along with your intent handler, and extract the generated content using either the schemas.intent()
and utterances()
functions on your app (if using the normal Developer portal), schemas.skillBuilder()
if using the new Skill Builder beta, or schemas.askcli()
if using the ask-cli
tool.
Schema Syntax
Pass an object with two properties: slots and utterances.
app;
slots
The slots object is a simple name: type
mapping. The type must be one of Amazon's built-in slot types, such as AMAZON.DATE
or AMAZON.NUMBER
.
custom slot types
Custom slot types are supported via the following syntax.
app;
This will result in the following utterance list.
sampleIntent airport information for {CustomSlotName}
sampleIntent airport status for {CustomSlotName}
Note that the "CustomSlotType" type values must be specified in the Skill Interface's Interaction Model for the custom slot type to function correctly.
custom slot type values
If you have custom slot types, you can define your custom slot type values as well. Custom values can either be simple strings, or more full-fledged objects if you want to take advantage of Skill Builder features like synonyms. If using synonyms, you can also take advantage of utterance expansion from alexa-utterances (including dictionary), as described below.
testApp;
OR
testApp;
utterances
The utterances syntax allows you to generate many (hundreds or even thousands) of sample utterances using just a few samples that get auto-expanded. Any number of sample utterances may be passed in the utterances array.
This module internally uses alexa-utterances to expand these convenient strings into a format that alexa understands. Read the documentation there for a thorough set of examples on how to use this.
Using a Dictionary
Several intents may use the same list of possible values, so you want to define them in one place, not in each intent schema. Use the app's dictionary.
appdictionary = "colors":"red""green""blue";..."my favorite color is {colors|FAVEORITE_COLOR}""I like {colors|COLOR}"
Generating Schema and Utterances Output
Intent Schema Syntax
If you are using the normal Amazon developer portal, the schemas.intent()
and utterances()
functions will generate an
intent schema JSON string and a list of utterances, respectively.
See example/express.js for one way to output this data.
// returns a String representation of an Intent Schema JSON objectappschemas "intents": "intent": "MyColorIsIntent" "slots": "name": "Color" "type": "AMAZON.Color" app MyColorIsIntent my color is dark brown|ColorMyColorIsIntent my color is green|ColorMyColorIsIntent my favorite color is red|ColorMyColorIsIntent my favorite color is navy blue|ColorWhatsMyColorIntent whats my colorWhatsMyColorIntent what is my colorWhatsMyColorIntent say my colorWhatsMyColorIntent tell me my colorWhatsMyColorIntent whats my favorite colorWhatsMyColorIntent what is my favorite colorWhatsMyColorIntent say my favorite colorWhatsMyColorIntent tell me my favorite colorWhatsMyColorIntent tell me what my favorite color is
Skill Builder Syntax
If you are using the Skill Builder Beta, the schemas.skillBuilder()
function will generate a single schema JSON string
that includes your intents with all of their utterances
appschemas "intents": "name": "MyColorIsIntent" "samples": "my color is {dark brown|Color}" "my color is {green|Color}" "my favorite color is {red|Color}" "my favorite color is {navy blue|Color}" "slots": "name": "Color" "type": "AMAZON.Color" "samples": "types": "name": "MyCustomColor" "values": "id": null "name": "value": "aquamarine" "synonyms": "aqua" "seafoam" "teal" ;
ask-cli Schema
The ask-cli tool accepts a schema in the same format as the Skill Builder, but is structured slightly differently. The schemas.askcli()
function generates a JSON string suitable to be used with the ask deploy
command.
This schema format requires you to specify the invocation name for your skill. You can set this for your skill by setting app.invocationName
. If you need to use different invocation names for the same skill (e.g. you have both a staging and production version), the schema function itself can take in an invocation name which overwrites the app's default.
appschemas "interactionModel": "languageModel": "invocationName": "favorite color" "intents": "name": "MyColorIsIntent" "samples": "my color is {dark brown|Color}" "my color is {green|Color}" "my favorite color is {red|Color}" "my favorite color is {navy blue|Color}" "slots": "name": "Color" "type": "AMAZON.Color" "samples": "types": "name": "MyCustomColor" "values": "id": null "name": "value": "aquamarine" "synonyms": "aqua" "seafoam" "teal" ;
Cards
The response.card(Object card)
method allows you to send Home Cards on the Alexa app, the companion app available for Fire OS, Android, iOS, and desktop web browsers.
The full specification for the card
object passed to this method can be found here.
The full specification for the permission card can be found here.
Cards do not support SSML.
If you just want to display a card that presents the user to link their account call response.linkAccount()
as a shortcut.
Card Examples
Display text only, aka Simple.
response;
Display text and image, aka Standard.
Make sure to read the restrictions on hosting the images. Must support CORS AND SSL cert signed by an Amazon approved certification authority.
response;
Display a card that presents the user to grant information to your skill, aka AskForPermissionsConsent.
If the request was for the country and postal code, then the permissions value in this response will be read::alexa:device:all:address:country_and_postal_code
.
response;
Custom Directives
The response.directive(Object directive)
method allows you to set custom directive objects to devices to perform a specific device-level actions.
The full specification for the directive
object passed to this method can be found here.
The alexa-app
library has special handling for AudioPlayer directives, so you only need to use this method for more general custom directives.
The response.directive
adds your directive object to the directives array in the response. To clear the directives from the response, call response.getDirectives().clear()
.
Dialog
The full specification for the dialog directives that can be used can be found here. See Custom Directives above for an example on manually sending dialog directives.
Note that skills must meet Alexa's requirements to use the Dialog
directive.
The alexa-app
library has special handling for enabling Alexa to handle Dialog directly. To
configure alexa-app
to delegate dialog to Alexa, enable the handling
per-intent via the schema:
app;
dialog object
// return the Dialog objectDialog request // return the intent's dialogStateString dialogdialogState // check if the intent's dialog is STARTEDBoolean dialog // check if the intent's dialog is IN_PROGRESSBoolean dialog // check if the intent's dialog is COMPLETEDBoolean dialog
Error Handling
When handler functions throw exceptions, they will trigger a rejection in the promise chain. If the response has not already been sent, .post
will be triggered which will allow you to force a successful response. If post
does not alter the response, then a failed response will be sent. You can use this to throw an exception to or call return response.fail("message")
to force a failure, but this does not generate a response compatible with Alexa.
The .error
handler method will capture any errors in the chain. The default behavior of .error
is to trigger response.send
if the response has not already been sent, but you can force or continue failure by returning a rejected promise or throw
ing inside the error handler. Returning a promise allows you to do asynchronous operations in the error handler.
Ideally, you should catch errors in your handlers and respond with an appropriate output to the user. Any exceptions can be handled by a generic error handler which you can define for your app. If you want error handling to be asynchronous, it must return a promise.
app { response;};
If you do want exceptions to bubble out to the caller (and potentially cause Express to crash, for example), you can throw the exception from the error handler.
app { console; throw exception;};
Echo Show Support
With the addition of custom directives and support for display elements being selected, this library fully supports the Echo Show. Note that it is up to the developer to detect if the device can handle a display directive. If a display directive is returned to a non-visual device it will throw an error. One technique is to leverage the app.post
call and remove any directives if the device does not support a UI. For example:
app.post(req, res, type, exception) {
// If the device does not support display directives then remove them from the response
if (!system.supportsDisplay(req))) {
res.response.response.directives = []
}
}
Please refer to Amazon's documentation for the list of supported template markup.
Asynchronous Handlers Example
If an intent or other request handler (including pre
and post
, but not error
) will return a response later, it must a Promise
. This tells the alexa-app library not to send the response automatically.
If the Promise resolves, the response will be sent. If it is rejected, it is treated as an error.
app;
If you want to respond immediately, you can use return response.send()
to complete the respones. Using throw msg
or return response.fail(msg)
will trigger immediate failure. Note: .post
is still run once after response.send()
or response.fail()
are called.
app;
Customizing Default Error Messages
appmessagesNO_INTENT_FOUND = "Why you called dat intent? I don't know bout dat";
See the code for default messages you can override.
Read/write session data
app; app; // the session variables can be entirely cleared, or cleared by keyapp;
By default, alexa-app will persist every request session attribute into the response. This way, any session attributes you set will be sent on every subsequent request, as is typical in most web programming environments. If you wish to disable this feature, you can do so by setting app.persistentSession
to false
.
var app = "test";apppersistentSession = false;
Define a custom endpoint name for an app
When mapped to express, the default endpoint for each app is the name of the app. You can customize this using the second parameter to the app()
method.
var app = "hello" "myEndpointName";
All named apps can be found in the alexa.apps
object, keyed by name. The value is the app itself.
License
Copyright (c) 2016-2017 Matt Kruse
MIT License, see LICENSE for details.