[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report
[ ] Performance issue
[ ] Feature request
[x] Documentation issue or request
[ ] Other... Please describe:
Could you please create an example Alexa skill that's built as an express app, which shows how to pass request / context to handlers, instead of a lambda-only example? I'm dependent upon using Express to power my Skill (handling incoming https requests). So, it'd be helpful to see a best practices for building a skill this way.
// Not required, but suggest a fix/reason for the bug,
// or ideas how to implement the addition or change
// Provide a self-contained, concise snippet of code
// For more complex issues provide a repo with the smallest sample that reproduces the bug
// Including business logic or unrelated code makes diagnosis more difficult
I'm trying to upgrade my skill to the V2 SDK.
My skill does use persistence / dynamodb state management.
I'm building a multi-modal Skill for both Alexa & Google Home.
My skill is an express app that runs via lambda.
I'm building multiple skills, powered by similar lambdas, where the URL path of the endpoint changes what data the skill returns.
Hi @nealrs,
this is how I handle requests to the new ask-sdk on my local express development server. "ENVIRONMENT" comes from a environment variable. If it is set to "production", we are on aws lambda. If not, we are on my local environment and using express.
I'm not sure if it's a best practice - but it's working fine for me.
const Alexa = require('ask-sdk');
let skill;
if (ENVIRONMENT === 'production') {
exports.handler = async function (event, context) {
if (!skill) {
skill = Alexa.SkillBuilders.custom()
.addRequestHandlers(
HelloWorldHandler
)
.create();
}
return skill.invoke(event,context);
}
} else {
// Development environment - we are on our local node server
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.post('/', function(req, res) {
if (!skill) {
skill = Alexa.SkillBuilders.custom()
.addRequestHandlers(
HelloWorldHandler
)
.create();
}
skill.invoke(req.body)
.then(function(responseBody) {
res.json(responseBody);
})
.catch(function(error) {
console.log(error);
res.status(500).send('Error during the request');
});
});
app.listen(3000, function () {
console.log('Development endpoint listening on port 3000!');
});
}
Kind regards
Magnus
Hi all,
Thanks @maegnes for the solutions above. We will be looking into bringing first class support for express!
Closing this issue now.
Regards
Any news for first class support for express? (full example and so on)
Hi
Please could you post the code to create the HelloWorldHandler, including any requires. I keep getting AskSdk.GenericRequestDispatcher Error: Unable to find a suitable request handler.
Here is my node.js
//-----------------------------------------------------------------------------
// test alexa web hook server using express
//-----------------------------------------------------------------------------
"use strict"
//-----------------------------------------------------------------------------
// required modules
//-----------------------------------------------------------------------------
// need express to pick up IIS settings
const express = require("express")
// need body-parser to parse json
const bodyParser = require("body-parser")
// need ask-sdk for alexa app
const alexa = require('ask-sdk');
//-----------------------------------------------------------------------------
// alexa app set up
//-----------------------------------------------------------------------------
// define handler for alexa launch
const alexaLaunch = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.type === 'LaunchRequest'
},
handle(handlerInput) {
return (handlerInput) => {
handlerInput.responseBuilder.speak('Hello, how can I help you?')
.reprompt('Is there anyone there?')
.getResponse()
}
}
}
// define handler for each alexa intent
const askTime = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.type === 'IntentRequest'
&& handlerInput.requestEnvelope.intent.name === 'AskTime'
},
handle(handlerInput) {
return (handlerInput) => {
handlerInput.responseBuilder.speak('That is a really good question.')
.reprompt('I have no idea.')
.getResponse()
}
}
}
//-----------------------------------------------------------------------------
// web hook server set up
//-----------------------------------------------------------------------------
// web hook server for express
const server = express()
// set server port
server.set("port", process.env.PORT || 3000)
// set server using app with body parser for requests and responses
server.use(bodyParser.json({type: "application/json"}))
//todo: debug only handle server get requests not from alexa
server.get('', function (req, res) {
console.log("debug webhookAlexa get request")
res.send('Hello world')
})
// handle server post requests for alexa skills
server.post('', function(req, res) {
console.log("debug webhookAlexa post request")
console.log("debug webhookAlexa launch skill: " + JSON.stringify(alexaLaunch))
console.log("debug webhookAlexa ask time skill: " + JSON.stringify(askTime))
// create alexa skills
var skills = alexa.SkillBuilders.custom()
.addRequestHandlers(
alexaLaunch,
askTime
)
.create()
console.log("debug webhookAlexa skills:" + JSON.stringify(skills))
// invoke alexa skills
skills.invoke(req.body)
.then(function(responseBody) {
console.log("debug webhookAlexa response: " + JSON.stringify(respomseBody))
res.json(responseBody)
})
.catch(function(error) {
console.log("error webhookAlexa: " + error)
res.status(500).send('Error during the request')
})
})
// start server listening
server.listen(server.get("port"), function () {
console.log("info express server started on port", server.get("port"))
})
and my debug log:
info express server started on port \.pipe.............
debug webhookAlexa get request
debug webhookAlexa post request
debug webhookAlexa launch skill: {}
debug webhookAlexa ask time skill: {}
debug webhookAlexa skills:{"requestDispatcher":{"requestMappers":[{"requestHandlerChains":[{"requestHandler":{}},{"requestHandler":{}}]}],"handlerAdapters":[{}],"requestInterceptors":[],"responseInterceptors":[]}}
error webhookAlexa: AskSdk.GenericRequestDispatcher Error: Unable to find a suitable request handler.
debug webhookAlexa post request
debug webhookAlexa launch skill: {}
debug webhookAlexa ask time skill: {}
debug webhookAlexa skills:{"requestDispatcher":{"requestMappers":[{"requestHandlerChains":[{"requestHandler":{}},{"requestHandler":{}}]}],"handlerAdapters":[{}],"requestInterceptors":[],"responseInterceptors":[]}}
error webhookAlexa: AskSdk.GenericRequestDispatcher Error: Unable to find a suitable request handler.
debug webhookAlexa post request
debug webhookAlexa launch skill: {}
debug webhookAlexa ask time skill: {}
debug webhookAlexa skills:{"requestDispatcher":{"requestMappers":[{"requestHandlerChains":[{"requestHandler":{}},{"requestHandler":{}}]}],"handlerAdapters":[{}],"requestInterceptors":[],"responseInterceptors":[]}}
error webhookAlexa: AskSdk.GenericRequestDispatcher Error: Unable to find a suitable request handler.
debug webhookAlexa post request
debug webhookAlexa launch skill: {}
debug webhookAlexa ask time skill: {}
debug webhookAlexa skills:{"requestDispatcher":{"requestMappers":[{"requestHandlerChains":[{"requestHandler":{}},{"requestHandler":{}}]}],"handlerAdapters":[{}],"requestInterceptors":[],"responseInterceptors":[]}}
error webhookAlexa: AskSdk.GenericRequestDispatcher Error: Unable to find a suitable request handler.
The skills are not set correctly but I don't know what is wrong
Hi @LouiseNewell
I have created a small gist to show you how to implement the "HelloWorldIntent" in the same file. In my example the handler is stored inside the rest of the example code and you do not need to require any further files:
https://gist.github.com/maegnes/0fa08ef86c721bf5b62d0b5721b20136
If you would like your handler to be in a separate file create a file called "HelloWorldIntent.js" and paste the following code into the file:
// HelloWorldIntent.js
module.exports = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'HelloWorldIntent';
},
handle(handlerInput) {
const speechText = 'Hello World!';
return handlerInput.responseBuilder
.speak(speechText)
.withSimpleCard('Hello World', speechText)
.getResponse();
}
};
If you now look at my gist you could change the code from this ....
// https://gist.github.com/maegnes/0fa08ef86c721bf5b62d0b5721b20136
// Old code
const Alexa = require('ask-sdk');
let skill;
const HelloWorldHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'HelloWorldIntent';
},
handle(handlerInput) {
const speechText = 'Hello World!';
return handlerInput.responseBuilder
.speak(speechText)
.withSimpleCard('Hello World', speechText)
.getResponse();
}
};
// And so on
... to this ...
// https://gist.github.com/maegnes/0fa08ef86c721bf5b62d0b5721b20136
// New code
const Alexa = require('ask-sdk');
let skill;
// I assume that the file is in the same directory
const HelloWorldHandler = require('./HelloWorldIntent.js');
// And so on
Regards Magnus
Thanks Magnus - worked a charm - had seen this on another thread but somehow not managed to mash the two together. You're a star.
Hi all :) Just a quick question regarding APL.
So i'm using the latest node sdk for Alexa in combination with Express. I'm trying to do something with the TouchWrapper in APL. This is what is defined in my template:
{
"type": "TouchWrapper",
"item" : {
"paddingLeft": "@marginLeft",
"paddingRight": "@marginRight",
"type": "Text",
"text": "${payload.documentData.text}",
"style": "textStyleBody",
"textAlign": "${viewport.shape == 'round' ? 'center' : 'left'}"
},
"onPress": {
"type": "SendEvent",
"arguments": [
"itemPressed",
"honeyMustard"
]
}
}
When I open this view in the simulator, I can click on the item and I see a "User Touch Event" in the logging on the left, but I don't get any request at all to my express server. I even added the following:
router.all('/', (req, res, next) => {
console.log(req);
console.log('-'.repeat(50));
next();
});
but no request is send to my server. Is there something else I have to do to get this TouchWrapper working?
Hi all :) Just a quick question regarding APL.
So i'm using the latest node sdk for Alexa in combination with Express. I'm trying to do something with the TouchWrapper in APL. This is what is defined in my template:
{ "type": "TouchWrapper", "item" : { "paddingLeft": "@marginLeft", "paddingRight": "@marginRight", "type": "Text", "text": "${payload.documentData.text}", "style": "textStyleBody", "textAlign": "${viewport.shape == 'round' ? 'center' : 'left'}" }, "onPress": { "type": "SendEvent", "arguments": [ "itemPressed", "honeyMustard" ] } }When I open this view in the simulator, I can click on the item and I see a "User Touch Event" in the logging on the left, but I don't get any request at all to my express server. I even added the following:
router.all('/', (req, res, next) => { console.log(req); console.log('-'.repeat(50)); next(); });but no request is send to my server. Is there something else I have to do to get this TouchWrapper working?
Nevermind! Got it working! needed to add a reprompt, because alexa wasn't listening anymore. Apparently touch events only work when Alexa is still listening.
Hi @nealrs,
this is how I handle requests to the new ask-sdk on my local express development server. "ENVIRONMENT" comes from a environment variable. If it is set to "production", we are on aws lambda. If not, we are on my local environment and using express.
I'm not sure if it's a best practice - but it's working fine for me.
const Alexa = require('ask-sdk'); let skill; if (ENVIRONMENT === 'production') { exports.handler = async function (event, context) { if (!skill) { skill = Alexa.SkillBuilders.custom() .addRequestHandlers( HelloWorldHandler ) .create(); } return skill.invoke(event,context); } } else { // Development environment - we are on our local node server const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.json()); app.post('/', function(req, res) { if (!skill) { skill = Alexa.SkillBuilders.custom() .addRequestHandlers( HelloWorldHandler ) .create(); } skill.invoke(req.body) .then(function(responseBody) { res.json(responseBody); }) .catch(function(error) { console.log(error); res.status(500).send('Error during the request'); }); }); app.listen(3000, function () { console.log('Development endpoint listening on port 3000!'); }); }Kind regards
Magnus
Thank you for your example.
Can you update the example with another one with the verification requested of the signature?
https://developer.amazon.com/it/docs/custom-skills/host-a-custom-skill-as-a-web-service.html#cert-verify-signature-certificate-url
I'm trying to use the express adapter provided but i can't understand how to integrate it. Now i'm trying to integrate a manual signature verification. Below you can find my integration. What is textBody param?
```js
app.post('/alexa', function(req, res) {
if (!skill) {
skill = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestIntentHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
)
.addErrorHandlers(ErrorHandler)
.create();
}
// This code snippet assumes you have already consumed the request body as text and headers
try {
await new SkillRequestSignatureVerifier().verify(textBody, requestHeaders);
await new TimestampVerifier().verify(textBody);
} catch (err) {
// server return err message
}
skill.invoke(req.body)
.then(function(responseBody) {
res.json(responseBody);
})
.catch(function(error) {
console.log(error);
res.status(500).send('Error during the request');
});
});
Most helpful comment
Hi @nealrs,
this is how I handle requests to the new ask-sdk on my local express development server. "ENVIRONMENT" comes from a environment variable. If it is set to "production", we are on aws lambda. If not, we are on my local environment and using express.
I'm not sure if it's a best practice - but it's working fine for me.
Kind regards
Magnus