https Request in Alexa Skill - node.js

I can't seem to get HTTP requests to work in my alexa skill, here is the relevant sample code:
var https = require('https');
...
function getTreeFact(callbackFunction){
var url = 'https://alexa.phl.chs.network/treefacts/index.php';
https.get(url, function(res){
var body = '';
res.on('data', function(chunk){
body += chunk;
});
res.on('end', function(){
var gameResponse = JSON.parse(body);
callbackFunction(gameResponse);
});
}).on('error', function(e){
// Handle error
});
}
...
this.getTreeFact(function (responseMessage){
this.emit(':tell', responseMessage.message);
});
I have no idea what I am doing wrong, I think I am making the HTTP request correctly. I know the skill works without this (simply commenting out the last three lines and replacing with just this.emit(':tell', 'hello') works fine).

Let me explain my below code...Here I have used https request in the "LaunchRequest"
which in turns gives me a response and with that response and I'm making my alexa to speak
note: jsonplaceholder.typicode.com is very useful for testing your https req and response
Simply use this code in your aws online editor console make sure you type the right intent name and invocation name.
exports.handler = (event, context, callback) => {
var speechResult;
switch (event.request.type) {
case "LaunchRequest":
var resultis;
const querystring = require('querystring');
const https = require('https');
var postData = querystring.stringify({
'msg' : 'Hello World!'
});
var options = {
hostname: 'jsonplaceholder.typicode.com',
path: '/posts',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postData.length
}
};
var req = https.request(options, (res) => {
//console.log('statusCode:', res.statusCode);
//console.log('headers:', res.headers);
res.on('data', (d) => {
//console.log("my data is "+d);
var obj = JSON.parse(d);
var resul = obj.msg;
resultis = JSON.stringify(resul);
context.succeed(generateResponse(buildSpeechletResponse(resultis, true)));
});
});
req.on('error', (e) => {
console.error(e);
});
req.write(postData);
req.end();
break;
case "IntentRequest":
switch (event.request.intent.name) {
case "MyIntent":
var a = "are you ready";
context.succeed(generateResponse(buildSpeechletResponse(a, true)))
break;
}
break;
}
}
//Alexa Speech function
buildSpeechletResponse = (outputText, shouldEndSession) => {
return {
outputSpeech: {
type: "PlainText",
text: outputText
},
shouldEndSession: shouldEndSession
}
}
generateResponse = (speechletResponse) => {
return {
version: "1.0",
response: speechletResponse
}
}

Alexa's official github page has a very thorough documentation on api calls. Check their Cooking list skill documentation it covers all of the get and post requests https://github.com/alexa/alexa-cookbook
and https://github.com/alexa/ main repo for other samples.

You better use a promise version, for example
https://github.com/request/request-promise

To me it looks like
this.emit(':tell', responseMessage.message);
should be
this.emit(':tell', responseMessage);
I can't see any .message in this
var gameResponse = JSON.parse(body);
callbackFunction(gameResponse);

Related

AWS API Gateway with Lambda HTTP GET Request (Node.js) 502 Bad Gateway

I'm new to AWS lambda functions and NodeJS. I'm trying to create an API Gateway call to a Lambda function that calls an external API and return some JSON data. It took me a while but I was finally able to get something to work based on this post:
AWS Lambda HTTP POST Request (Node.js)
The problem was the API Gateway kept erroring with a 502 Bad Gateway; which turns out to be that the JSON response was malformed. In the post I referenced above everyone seem to have success with just returning the JSON as-is, but I had to follow the instructions here to fix my issue:
https://aws.amazon.com/premiumsupport/knowledge-center/malformed-502-api-gateway/
My question is: if you look at the last 10 lines of my code that finally worked I had to reformat my response, as well as use a callback in a async function. I am new to nodeJS and Lambda but it looks wrong to me, even though it works. The post I referenced seem to have much more elegant code, and I hope someone can tell me what I am doing wrong.
const https = require('https');
var responseBody = {"Message": "If you see this then the API call did not work"};
const doGetRequest = () => {
return new Promise((resolve, reject) => {
const options = {
host: 'my.host.com',
path: '/api/v1/path?and=some&parameters=here',
method: 'GET',
headers: {
'Authorization': 'Bearer token for testing',
'X-Request-Id': '12345',
'Content-Type': 'application/json'
}
};
var body='';
//create the request object with the callback with the result
const req = https.request(options, (res) => {
res.on('data', function (chunk) {
body += chunk;
});
res.on('end', function () {
console.log("Result", body.toString());
responseBody = body;
});
resolve(JSON.stringify(res.statusCode));
});
// handle the possible errors
req.on('error', (e) => {
reject(e.message);
});
//finish the request
req.end();
});
};
exports.handler = async (event, context, callback) => {
await doGetRequest();
var response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(responseBody),
"isBase64Encoded": false
};
callback(null, response);
};
I see couple of things.
We need to get the values from method doGetRequest and use the response, we can do that by await response = doGetRequest() or doGetRequest.then(), since we ant to capture errors as well, i went with second method.
We also need to resolve or reject the actual response from within promise.
I tested with a different api(with url of this question). Here is the updated code.
const https = require('https');
var responseBody = {"Message": "If you see this then the API call did not work"};
const doGetRequest = () => {
return new Promise((resolve, reject) => {
const options = {
host: 'stackoverflow.com',
path: '/questions/66376601/aws-api-gateway-with-lambda-http-get-request-node-js-502-bad-gateway',
method: 'GET'
};
var body='';
//create the request object with the callback with the result
const req = https.request(options, (res) => {
res.on('data', function (chunk) {
body += chunk;
});
res.on('end', function () {
console.log("Result", body.toString());
resolve(body);
});
});
// handle the possible errors
req.on('error', (e) => {
reject(e.message);
});
//finish the request
req.end();
});
};
exports.handler = (event, context, callback) => {
console.log('event',event, 'context',context);
doGetRequest().then(result => {
var response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(result),
"isBase64Encoded": false
};
callback(null, response);
}).catch(error=> {
callback(error);
})
};

Issue posting API request from AWS Lambda with nodejs

completely beginner question here, but im stuck for hours, hope someone can help!
I'm building some thing over AWS API Gateway + Lambda, where I receive a POST request on AWS and I send some data to another API.
I'm using https from NodeJS (from examples i found here on stackoverflow) but it doesnt seem to be working...I'm testing by sending it to a webhook inbox in beeceptor
Could you give me some light?
exports.handler = async (event) => {
if(event.httpMethod == 'POST'){
return pedido(event);
}
};
var aid = '';
var cep = '';
const pedido = event => {
let body = JSON.parse(event.body);
var aid = body.cid;
//var sku = body.items.id
var cep = body.cep;
callapi(cep,aid);
console.log("teste cep ", body.cep);
return{
statusCode: 200,
body: JSON.stringify({
message: body.cep,
convid: aid
})
};
};
function callapi(cep,aid){
const https = require('https');
const data = JSON.stringify({
message: cep,
convid: aid,
test: 123
});
console.log("data is ", data);
const options = {
hostname: 'testbot.free.beeceptor.com',
//port: 443,
path: '/',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
};
console.log("code was here ");
var req = https.request(options, (res) => {
console.log('req:', req);
console.log('res:', res);
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
res.on('data', (d) => {
process.stdout.write(d);
});
});
console.log('req:', req);
req.on('error', (e) => {
console.error(e);
});
req.write(data);
req.end();
}
There's at least one problem with your code:
The callapi function is making a request and this request is using a callback to notify you about a response. However, you are not waiting for it in your Lambda code and hence you won't see its response in your logs. Add appropriate awaits or Promises to it, so your code won't return before you've received a response.
The structure of your code could look similar to this:
exports.handler = async (event) => {
if (event.httpMethod === 'POST') {
return await pedido(event);
}
};
async function pedido(event) {
// init vars...
// wait for your API call
await callapi(cep, aid);
// then return a response
return {...}
}
async function callapi(cep, aid) {
// init vars like https and others...
// then use a promise and resolve it when you receive the request's callback (= response) or an error
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
// handle response however you like ...
// then resolve the promise when you're done
resolve();
});
req.on('err', (e) => {
// reject in case the request fails
reject(e);
});
});
}
Does this solve your problem? If not, having some more logs of your method and a simplified code example would help a lot!

How do I do a request.write(body) without the body?

I am using the code as shown below. However I want to include the body in the query string (which I can do fine) - however, I am unsure how to restructure thehttps.request so as to remove request.write(body) - simply using request.write() does not work as it requires a string.
Can someone help?
Thanks
var body = JSON.stringify( json.text );
const params = {
'q': body,
};
var requestUrl = url.parse( URL + queryStringify(params) );
const requestOptions = {
hostname: requestUrl.hostname,
path: requestUrl.path,
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
};
var request = https.request(requestOptions, function(res) {
var data = "";
res.on('data', function (chunk) {
//do stuff
});
res.on('end', function () {
//do stuff
});
});
request.write(body);
request.end();
To do that, you can set your params in path field, with querystring like this :
Set your query string params into requestOptions
const querystring = require('querystring');
requestOptions.path = `/your/Path?${querystring.stringify({firstName: 'John', lastName: 'doe'})}`;
// Result is '/your/Path?firstName=John&lastName=doe'
Then, do your request
var request = https.request(requestOptions, function(res) {
res.on('data', function(chunk) {
// Do stuff
});
res.on('end', function() {
// Do stuff
});
});
request.on('error', function (err) {
// Throw err
});
request.end();
Hope it helps.

NodeJS http post request read timeout

I'm trying to execute the following code inside AWS Lambda which only makes a POST http request to an ElasticSearch.
The problem I'm facing is that it seems the nodejs request has a read timeout and the response is almost always cut and an error is thrown. I've checked that the problem is not related with AWS Lambda timeout which is set to 10 seconds and the code throws an error in less than a second.
As you can see, I've tried to put a timeout to 5secs but I think that's a connection timeout and not a read timeout.
What am I doing wrong?
var http = require('http');
exports.handler = (event, context, callback) => {
var options = {
hostname: '172.31.40.10',
port: 9200,
path: '/articles/es/_search?_source=reference',
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
};
var req = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (body) {
var parsed = JSON.parse(body);
var b = [];
for (var i = 0; i < parsed.hits.hits.length; i++) {
b.push(parsed.hits.hits[i]._source.reference);
}
var response = {
statusCode: '200',
body: JSON.stringify(b),
headers: {
'Content-Type': 'application/json',
}
};
callback(null, response);
});
});
req.on('error', function(e) {
callback(new Error('fallo'));
});
req.setTimeout(5000, function() {req.abort;})
req.on('socket', function (socket) {
socket.setTimeout(5000);
socket.on('timeout', function() {
req.abort();
});
});
req.write(MY_QUERY_HERE);
req.end();
};
I think you should let the stream of incoming data finish before performing any data manipulation.
Example :
var http = require('http');
//var _ = require('underscore');
function MyPostRequest(callback) {
var options = {
hostname:'172.31.40.10',
port:9200,
path:'/articles/es/_search?_source=reference',
method:'POST',
headers:{'Content-Type':'application/json'}
};
http.request(options, function(res) {
var tmpstore = ''; //temp. data storage
//:Store the continuous incoming data stream
res.on('data', function(d){tmpstore += d;});
//:Data reception is done, use it now...
res.on('end', function() {
var parsed = JSON.parse(tmpstore); var b = [];
for (var i = 0; i < parsed.hits.hits.length; i++) {
b.push(parsed.hits.hits[i]._source.reference);
}
/* //I suggest using underscore module :
_.each(parsed.hits.hits,function(element, index, list){
b.push(element._source.reference);
});
*/
var response = {
statusCode:'200',
body:JSON.stringify(b),
headers:{'Content-Type':'application/json'}
};
callback(null, response);
});
//:Response contained an error
res.on('error', function(e){/*error handling*/callback(e,null);});
});
}

Does AWS Lambda (NodeJS) not allow http.request or https.request?

I am attempting to make a request to another API from a Lambda. I am finding that using the NodeJS http and https modules allow for GET requests but any others (e.g. POST) do not work; POST coincidentally is the only method I need to work for the service I am attempting to call.
Here is a working example of Lambda performing a GET and receiving a 200 response:
const https = require('https')
function handler(event, context, callback) {
const options = {
hostname: 'encrypted.google.com'
}
https
.get(options, (res) => {
console.log('statusCode:', res.statusCode);
res.on('end', callback.bind(null, null))
})
.on('error', callback);
}
exports.handler = handler
So that proves that he request is allowed. However, if the script attempts to make the same request using the .request() method of the https (or https) lib/module the request never finishes and the Lambda times out.
const https = require('https')
function handler(event, context, callback) {
const options = {
hostname: 'encrypted.google.com',
method: 'GET'
}
https
.request(options, (res) => {
console.log('statusCode:', res.statusCode);
res.on('end', callback.bind(null, null))
})
.on('error', callback);
}
exports.handler = handler
I don't know what I am doing wrong. The call https.request() silently fails - doesn't throw an error - and nothing is reported in the log.
The problem was that I was never completing the request with req.end().
const https = require('https')
function handler(event, context, callback) {
const options = {
hostname: 'encrypted.google.com',
method: 'GET'
}
https
.request(options, (res) => {
console.log('statusCode:', res.statusCode);
res.on('end', callback.bind(null, null))
})
.on('error', callback)
.end(); // <--- The important missing piece!
}
exports.handler = handler
Please try this one if your API is HTTPS,
var url = 'HTTPS URL HERE';
var req = https.get(url, (res) => {
var body = "";
res.on("data", (chunk) => {
body += chunk
});
res.on("end", () => {
var result = JSON.parse(body);
callBack(result)
});
}).on("error", (error) => {
callBack(err);
});
}
And if it is HTTP then,
var url = 'HTTP URL HERE';
var req = http.get(url, (res) => {
var body = "";
res.on("data", (chunk) => {
body += chunk
});
res.on("end", () => {
var result = JSON.parse(body);
callBack(result)
});
}).on("error", (error) => {
callBack(err);
});
}
Please don't fogot to add package require('https') / require('http')
The POST method is done by the request method.
This is the lambda code:
const https = require('https');
const options = {
hostname: 'Your host name',
path: '/api/v1/Login/Login',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body : JSON.stringify({
'email': 'hassan.uzair9#gmail.com',
'password': 'Asdf1234.',
})
};
var result;
try{
result = await https.request(options);
console.log("result.....",result);
}catch(err){
console.log("err......",err);
}

Resources