So, I'm trying to do a basic thing: Connect to an external REST-API from my AWS Lambda script.
This API hosts a list of holidays.
But, whenever I try to execute the code it just times out (max lambda execution time reached).
So I created this wrapper function, that is capable of handling 4 different ways of doing GET requests, but all of them perform the same.
const request = require('request')
const https = require('https')
const axios = require('axios')
const superagent = require('superagent')
let test = (type = "") => {
return new Promise((resolve, reject) => {
debug("Fetching with: " + type)
const d = new Date()
if(type == "superagent"){
superagent.get('https://holidayapi.pl/v1/holidays?country=DK&year=' + d.getFullYear())
.query({ country: 'DK', year: '2019' })
.end((err, res) => {
if (err) {
console.log(err)
reject(err)
} else {
console.log(res)
resolve(res)
}
})
} else if(type == "axios"){
axios.get('https://holidayapi.pl/v1/holidays?country=DK&year=' + d.getFullYear())
.then(response => {
debug(response)
resolve(response)
})
.catch(error => {
console.log(error)
reject(error)
})
} else if(type == "https"){
const req = https.get("https://holidayapi.pl/v1/holidays?country=DK&year=" + d.getFullYear(), (resp) => {
let data = ''
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk
})
// The whole response has been received. Print out the result.
resp.on('end', () => {
console.log(JSON.parse(data).explanation)
resolve([])
})
})
.on('error', (e) => {
debug(e)
reject(e.message)
})
req.end()
} else if(type == "request"){
request('https://holidayapi.pl/v1/holidays?country=DK&year=' + d.getFullYear(), { json: true }, (err, res, body) => {
debug(err)
debug(res)
debug(body)
if (err) reject(err)
else resolve(body.holidays)
})
} else {
reject("Mangler type")
}
})
}
exports.connect_test = (event, context, callback) => {
test(event.pathParameters.type)
.then((rsp) => {
callback(null, JSON.stringify(rsp, null, 2))
})
.catch((err) => {
callback(null, JSON.stringify(err, null, 2))
})
}
The debug function is a map to console.log, that checks if the NODE_ENV is "dev".
I presume the default VPC has got internet connectivity, So if it's in the default it should just work (just increase the default timeout on the lambda to something reasonable).
If its inside a VPC then you need to have NAT Gateway/NAT instance configured for that VPC to have internet connection or pair with another VPC that has an internet access(Inside the VPC lambda need to have appropriate role and subnet).
This might help you aws docs
Related
I have the following axis post request
EXAMPLE 1
Front End
let data = this.itemsDuplicated;
console.log("length",data.length)
await axios({
method: "post",
url: `${url}/upDateTasksFromProgress`,
data: data
})
.then(response => {
console.log(response.data);
})
.catch(e => {
console.log(e);
});
Node
router.post("/upDateTasksFromProgress", (req, res) => {
let mysql = "";
req.body.forEach(el => {
mysql = `${mysql} update tasks set startDate = '${el.startDate}', endDate = '${el.endDate}', duration = ${el.duration} where id = ${el.id};`
});
console.log(mysql)
pool.getConnection(function (err, connection) {
if (err) {
connection.release();
resizeBy.send("Error with connection");
}
connection.query(mysql, function (error, result) {
if (error) {
console.log(error);
} else {
console.log(result)
res.json(result);
}
});
connection.release();
});
});
This works fine.
I also have:
EXAMPLE 2
Front End
let data = [];
this.tasks.forEach(el => {
let insert = {
id: el.id,
startDate: el.startDate,
endDate: el.endDate,
duration: el.duration,
parentId: el.parentId,
dependantOn: el.dependantOn,
fix: el.fix
};
data.push(insert);
});
console.log(data.length)
await axios({
method: "post",
url: `${url}/postTaskUpdates`,
data: data
})
.then(response => {
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
Back End
router.post("/postTaskUpdates", (req, res) => {
let mysql = "";
req.body.forEach(el => {
mysql = `${mysql} update tasks set startDate = '${el.startDate}', endDate = '${el.endDate}', duration = ${el.duration}, parentId = '${el.parentId}', dependantOn = '${el.dependantOn}' where id = ${el.id};`
});
pool.getConnection(function (err, connection) {
if (err) {
connection.release();
resizeBy.send("Error with connection");
}
connection.query(mysql, function (error, result) {
if (error) {
console.log(error);
res.json(error)
} else {
console.log(result)
res.json(result);
}
});
connection.release();
});
});
The second example gives me the following error:
“Access to XMLHttpRequest at
'https://www.testing-be.co.za/postTaskUpdates' from origin
'https://www.testing-fe.co.za' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource.”
Both are in the exact same Route File on the backend.
The only difference is the number of records.
In the first example there are 22 records.
In the second there are about 1300 records.
Besides the number of records, all else is as shown above and to my mind exactly the same.
Any suggestions would be greatly appreciated.
Yes, I have seen many other questions and answers. I know I need to use a callback response. However, I still don't get how to do this particular example. Most examples involve a callback response that logs something or the post has hundreds of different answers.
How do I return the request response from getPageData?
var url = "myurl";
var name = await getPageData(url);
// wait until I get name and then do stuff with name
function getPageData(url)
{
const https = require('https');
https.get(url, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
var name = JSON.parse(data);
// what do I do here to get name out?
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}
await can only be used in async functions. You can however return a promise from getPageData and "await" using chained then:
Use the Promise object:
const https = require('https');
var url = "myurl";
var name;
getPageData(url)
.then(data => { name = data; /*This is the scope in which you would use name*/ })
.catch(err => { console.log('Error occured', err); });
async function getPageData(url) {
return new Promise((resolve, reject) => {
https
.get(url, resp => {
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
const name = JSON.parse(data);
// what do I do here to get name out?
resolve(name);
});
})
.on('error', err => {
console.log(`Error: ${err.message}`);
reject(err);
});
});
}
The higher level solution here is to use a module for making http requests that already supported promises. You can see a list of many of them here. My favorite from that list is got() and you can use it to solve your problem like this:
function getPageData(url) {
return got(url);
}
// can only use await inside a function declared as async
async function someFunction() {
try {
let name = await getPageData(url);
console.log(name);
} catch(e) {
console.log(e);
}
}
The got() library does a whole bunch of things for you.
It is entirely based on promises so you can directly use await on the promise it returns.
It collects the whole response for you (you don't have to write your own code to do that).
If the response is JSON, it automatically parses that for you and resolves to the parsed Javascript object.
It automatically detects http or https URL and uses the right low level module.
And, it has dozens of other useful features (not needed in this example).
Or, if you want the lower level solution where you make your own promisified function for doing an https request, you can do this:
const https = require('https');
// can only use await inside a function declared as async
async function someFunction() {
const url = "myurl";
try {
let name = await getPageData(url);
console.log(name);
} catch(e) {
console.log(e);
}
}
function getPageData(url) {
return new Promise((resolve, reject) => {
https.get(url, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
try {
const name = JSON.parse(data);
resolve(name);
} catch(e) {
// JSON parsing error
reject(e);
}
});
}).on("error", (err) => {
console.log("Error: " + err.message);
reject(err);
});
}).on('error', (err) => {
console.log("Error: " + err.message);
reject(err);
});
}
I'm developing a simple chatbot on Amazon Alexa. The idea is to know if an item is on menu at a particular store.
function httpGet() {
return new Promise(((resolve, reject) => {
const options = {
headers: {
'auth_key':'xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx'
}
};
http.get("http://example.com/lookup/api/getitemlist?item=cake&storeid=50", options, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
resolve(JSON.parse(data));
});
}).on('error', (err) => {
reject(err);
});
}));
}
I'm following documentation on nodejs docs
The API works well in postman where the auth_key is passed in header.
Here's the error from amazon cloudwatch
The API responds with error message when the auth_key isnt present. Am I missing something? From reading the documentation. I thought this would work.
GetItemIntentHandler. I have to write more to handle the response. For now I'm only logging it. This is where I call the function httpGet();
const GetItemIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'GetItemIntent';
},
async handle(handlerInput){
const item = handlerInput.requestEnvelope.request.intent.slots.Item.value;
const response = await httpGet();
console.log("response reached us");
console.log(response);
return handlerInput.responseBuilder
.speak(` ${item}`)
.reprompt("What would you like?")
.getResponse();
}
};
I used the http.get(options[, callback]) method to get around.
Updated httpGet function I'm using now.
function httpGet() {
return new Promise(((resolve, reject) => {
var options = {
host: 'example.com',
path: '/lookup/api/getitemlist?item=cake&storeid=50',
port: null,
headers: {
'auth_key':'xxxxxxx-xxxxx-xxxxx-xxxxx-xxxxxxxxxx'
}
};
http.get(options, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
resolve(JSON.parse(data));
});
}).on('error', (err) => {
reject(err);
});
}));
}
I have made a function in my angular2+ component and the output comes first and the function runs later and hence the appropriate output which i want comes later. The function passes a variable parameter along with the http request to the back-end NodeJS. And returns the result. I want to find its length which i am able to retrieve. However, i want to call this parameter more than once by passing multiple parameters. So i defined it as Asynchronous function. The code is as -
app.component.ts
// Function 1
getNodesCount() {
console.log("INSIDE getNodesCount()")
if (this.selectedAPIName.length == 1) {
this.nodesObjQ1 = {
'relationObj': this.menuItem,
'nodeValue1': this.selectedAPIName[0]
}
this.callFunctionCount(this.nodesObjQ1).then((rs: any[])
=> {
this.nodesObjL1 = rs;
});
console.log("this.nodesObjL1 =", this.nodesObjL1)
}
}
//Function 2
async callFunctionCount(trueNodesObject) {
console.log("nodesObj =", trueNodesObject);
await new Promise(next => {
this.http.get("http://localhost:3003/seekExtraction/nodesObj/" +
JSON.stringify(trueNodesObject))
.map(Response => Response)
.catch((err) => {
console.log("err =", err)
return Observable.throw(err);
})
.subscribe((res: Response) => {
console.log("XXXXXXXXXXXX Response on /seekExtraction",
res);
this.nodesInfo = res;
this.nodesLength = this.nodesInfo.records.length
next()
})
});
console.log("return this.nodesLength =", this.nodesLength)
return this.nodesLength;
}
Major Outputs -
this.nodesObjL1 = undefined
return this.nodesLength = 2
Please help to retrieve this value -
this.nodesObjL1
after this value
this.nodesInfo.records.length= 2
actual minimal example would have helped ensure clean code... but give this a go...
getData = function (trueNodesObject) {
return new promise((resolve, reject) => {
this.http.get("http://localhost:3003/seekExtraction/nodesObj/" +
JSON.stringify(trueNodesObject))
.map(Response => Response)
.catch((err) => {
console.log("err =", err)
return Observable.throw(err);
reject(err);
})
.subscribe((res: Response) => {
console.log("XXXXXXXXXXXX Response on /seekExtraction",
res);
this.nodesInfo = res;
this.nodesLength = this.nodesInfo.records.length
resolve(this.nodesLength);
next()
});
})
}
async function callFunctionCount(trueNodesObject) {
console.log("nodesObj =", trueNodesObject);
const someVal = await getData(trueNodesObject);
console.log("return this.nodesLength =", someVal)
}
Getting the first [ const p1] HTTPS request, but unable to fetch the second one [const p2] its showing me undefined.Where im missing
function fetchJSON(url) {
return new Promise((resolve, reject) => {
request(url, function(err, res, body) {
if (err) {
reject(err);
} else if (res.statusCode !== 200) {
reject(new Error('Failed with status code ' + res.statusCode));
} else {
resolve(JSON.parse(body));
}
});
});
}
router.get('/news-and-media',function(req,res,next){
const p1 = fetchJSON('http://example.com/wsplus/abs/123');
const p2 = fetchJSON('http://example.com/blsd/blog_posts/312');
Promise.all([p1],[p2]).then((data) => {
console.log(data[0]); // getting data
console.log(data[1]); // this giving me undefined
res.render("news-and-media", { getdata: data[0],banner:data[1]} );
}).catch(err => console.error('There was a problem', err));
});
Don't use
Promise.all([p1], [p2])
but
Promise.all([p1, p2])
According to the Promise.all() documentation which is saying :
Promise.all(iterable);