Using data from an aysnc Javascript http request? (aws serverless) - javascript

I'm using the nodejs serverless module to create a lambda aws function.
'use strict';
const request = require('request');
const options = {
url: 'https://api.mysportsfeeds.com/v2.0/pull/nfl/2018-regular/games.json',
method: 'GET',
headers: {
"Authorization": "Basic " + Buffer.from("1da103"
+ ":" + "MYSPORTSFEEDS").toString('base64')
}
}
//this is automatically called by aws
module.exports.hello = async (event, context) => {
let result;
request.get(options, (error, response, body) => {
result = JSON.parse(body).lastUpdatedOn; //never happens cuz of async
});
return {
statusCode: 200,
body: JSON.stringify({
message: 'Go Serverless v1.0! Your function executed successfully!',
input: result,
}),
};
};
The problem I'm having is that I can't return output from the get request, because the assignment to the result variable (in the async get request) happens after the return statement. I don't think I can turn the outer function into a callback function for the get request. How could I work around this?

An alternative could be to extract the request logic and put it into a new function.
Remember, you need to catch any errors, so use a try-catch block for doing that.
'use strict';
const request = require('request');
const options = {
url: 'https://api.mysportsfeeds.com/v2.0/pull/nfl/2018-regular/games.json',
method: 'GET',
headers: {
"Authorization": "Basic " + Buffer.from("1da103"
+ ":" + "MYSPORTSFEEDS").toString('base64')
}
};
function getResult() {
return new Promise(function (resolve, reject) {
request.get(options, (error, response, body) => {
if (error) return reject(error);
resolve(JSON.parse(body).lastUpdatedOn); //never happens cuz of async
});
});
}
//this is automatically called by aws
module.exports.hello = async (event, context) => {
let result = await getResult();
return {
statusCode: 200,
body: JSON.stringify({
message: 'Go Serverless v1.0! Your function executed successfully!',
input: result,
}),
};
};

Related

Response is send immediately and does not wait for await

I want to wait for the getAllData function to be finished, but that does not work.
let _partnerToken;
async function getAllData(dealerId,id){
let partnerToken;
var options = {
'method': 'GET',
'url': _url + '/' + dealerId,
};
request.get(options, async function (error, response) {
if (error) throw new Error(error);
partnerToken = response.body.slice(17,-2);
await getCarData(partnerToken,id);
await getCarImages(partnerToken, id);
_partnerToken = partnerToken;
})
}
Here the post request where I want to call this function and send a response when the data is all loaded and the function is finished.
app.post('/api/urlData', async function(req,res){
const str = CircularJSON.stringify(req);
await getAllData(req.body.dealerId, req.body.vin);
res.send(req)
});
The problem is that it does the response immediately, so there is no data for the frontend.
My goal is to send a signal with the response to my frontend, that all the data has loaded and can then be displayed.
async functions always must return a Promise. If you don't return a Promise, the runtime implicitly creates and returns one resolving to your return value (in your case undefined).
The reason why the call to api/urlData happens immediately without waiting for the other requests to be completed in getAllData() is that you're calling request.get(...) passing a callback function. This function works asynchronously, but for the runtime it's an ordinary synchronous function call.
To solve this problem you have to create and return a Promise manually in getAllData() and call the resolve() callback after all requests have been completed:
async function getAllData(dealerId, id) {
return new Promise<any>((resolve, reject) => {
const options = {
'method': 'GET',
'url': _url + '/' + dealerId,
};
request.get(options, async function (error, response) {
if (error) {
reject(error);
return;
}
let partnerToken = response.body.slice(17,-2);
await getCarData(partnerToken,id);
await getCarImages(partnerToken, id);
_partnerToken = partnerToken;
resolve(returnValue); // <--- your return value
});
});
}
await getAllData(1, 2)
Solution
I started working with Promises and now the the responds waits for the functions to be finished.
Here is one of the new methods:
async function getCarData(partner, id) {
const options = {
url: url + '/?partnertoken=' + partner + '&fahrzeug_id=' + id,
headers: {
'x-api-key': key
}
};
return new Promise(function (resolve, reject){
request.get(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
let res = JSON.parse(body);
resolve(res.data[0]);
}else
reject(error);
});
})
}
app.post('/api/urlData', async function (req, res) {
let vin = req.body.vin;
let dealerId = req.body.dealerId;
const str = CircularJSON.stringify(req);
_partnerToken = await getPartnerToken(dealerId);
_data = await getCarData(_partnerToken,vin);
_dataImage = await getCarImages(_partnerToken,vin);
res.send(str)
});
I resturctured it so that every of the three methods returns a Promise.

Azure function doesn't return response after async function is complete

I'm trying to develop a function in VS Code that takes an url as input and returns the response after processing is complete. However, when this function is run, it returns nothing. I tried testing similar code in Webstorm and and confirm that it console.logs the results just fine. I'm new to node and promises so not sure what I'm doing wrong.
Edit - added return keyword before driver.get as per the suggestion from #hellikiam. Also added a simple log statement to confirm that the results available yet not being returned in body.
var AxeBuilder = require('#axe-core/webdriverjs'),
WebDriver = require('selenium-webdriver');
const chromedriver = require('chromedriver');
const chrome = require("selenium-webdriver/chrome");
const screen = {
width: 640,
height: 480
};
chrome.setDefaultService(new chrome.ServiceBuilder(chromedriver.path).build());
var driver = new WebDriver.Builder()
.forBrowser('chrome')
.setChromeOptions(new chrome.Options().headless().windowSize(screen))
.build();
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
const url = (req.query.url || (req.body && req.body.url));
return driver.get(url).then(function () {
new AxeBuilder(driver).analyze(function (err, results) {
resultsJson = JSON.stringify(results);
console.log(resultsJson)
context.res = {
status: 200, /* Defaults to 200 */
body: resultsJson,
headers: {
'Content-Type': 'application/json'
}
};
if (err) {
// Handle error somehow
}
});
});
context.done();
}
you didn't returned anything from exported module.
return driver.get(url).then(function () {//add return statement in here.
new AxeBuilder(driver).analyze(function (err, results) {
resultsJson = JSON.stringify(results);
context.res = {
status: 200, /* Defaults to 200 */
body: resultsJson,
headers: {
'Content-Type': 'application/json'
}
};
if (err) {
// Handle error somehow
}
});
});
I found the other problem. What you want to return is inside callback function.
You should wrap the callback with promise to get the result outside of the callback. Try this out brother.
return new Promise((resolve, reject) =>{
driver.get(url).then(function () {//add return statement in here.
new AxeBuilder(driver).analyze(function (err, results) {
resultsJson = JSON.stringify(results);
context.res = {
status: 200, /* Defaults to 200 */
body: resultsJson,
headers: {
'Content-Type': 'application/json'
}
};
resolve(resultsJson)//Any value you want to return
if (err) reject(err)
});
});
})
Calling this function outside of module with await statement
const foo = require('./my/path/foo')
(async()=>{
const params = 'my param'
const bar = await foo(params)
console.log(bar)
})()

Node.js Access Values from Anonymous function

Good morning!
I've been struggling to get a specific value returned from my function:
const getFolders = function (PID){
var token = getStoredToken()
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, function test(err, response, body){
return body
})
// I NEED THE RETURN VALUE OF THE ABOVE FUNCTION HERE SO I CAN ACCESS WHEN CALLING getFolders()
}
Is this possible? If so, how?
Thanks!
Usually there will be three ways dealing with asynchronous stuff:
callback
promise
async/await
callback:
const getFolders = function(PID, callback) {
var token = getStoredToken()
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, function(err, response, body) {
callback(body)
})
}
getFolders(pid, (v) => {
console.log(v)
})
promise:
const getFolders = function(PID, callback) {
return new Promise((resolve, reject) => {
var token = getStoredToken()
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, function(err, response, body) {
if (err) {
return reject(err)
}
resolve(body)
})
})
}
getFolders(pid)
.then(v => {
console.log(v)
}).catch(error => {
console.error(error)
})
async/await:
Due to async/await is actually a syntax sugar, the getFolders function is the same as using promise's, the difference is when you call it:
(async function() {
try {
let v = await getFolders(pid)
console.log(v)
} catch(e) {
console.error(e)
}
})()
Not sure if this solve your confusion.
The way you are expecting is wrong, the test function which you have passed to request.get method is a callback function which will execute in asychronous manner , it means whenever your API responds from the server, that callback function will get execute.
So before that you are expecting the response (body) below the request method, which is wrong.
In this case you just have to write some other function to call this get method and in callback function you can easily access that response or just write your code in that test function itself.
like below - :
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, function test(err, response, body){
// instead of returning body
// use the body here only
let result = body;
// your code here
})
Or the other way -:
const getFolders = function (PID){
var token = getStoredToken();
this.get(function(err, response, body){
// do whatever you want with the response now
updateFolder()
})
}
function get(callback){
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, callback)
}

How to: await / async in a node.js lambda function?

I am trying to create an async lambda function that makes a http Get call put it's not working correctly and I believe it has to do with it being async. If I remove await/async, and make it synchronous, my function works correctly. I'm not sure what I am doing wrong. Thank you for your help!
exports.handler = async function (event, context) {
await setBattery();
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
async function setBattery() {
'use strict';
const https = require('https');
const options = {
hostname: 'testsite.com',
port: 443,
path: '/proxy/api/setting?EM_OperatingMode=1',
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
};
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`);
res.on('data', d => {
process.stdout.write(d);
});
});
req.on('error', error => {
console.error(error);
});
req.end();
}
According with MDN Web Docs:
The async function declaration defines an asynchronous function, which
returns an AsyncFunction object. An asynchronous function is a
function which operates asynchronously via the event loop, using an
implicit Promise to return its result. But the syntax and structure of
your code using async functions is much more like using standard
synchronous functions.
So, You need to return a Promise object in order to be handled by your async function (You could also use another promise to handle it), I converted setBattery to a normal function and the return of this function is now a promise that will be handled by your handler (exports.handler). I haven't tested the code but it should work.
exports.handler = async function (event, context) {
await setBattery();
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
function setBattery() {
'use strict';
const https = require('https');
const options = {
hostname: 'testsite.com',
port: 443,
path: '/proxy/api/setting?EM_OperatingMode=1',
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
};
// Return it as a Promise
return new Promise((resolve, reject) => {
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`);
res.on('data', d => {
process.stdout.write(d);
// If successful
resolve(d);
});
});
req.on('error', error => {
console.error(error);
// If failed
reject(error);
});
req.end();
});
}

Returning nested promises to another function

I have a NodeJS application and I think I have an issue with returning from inside a nested Promise.
As below, the getToken function is working. It calls another function to retrieve a password. After this, it uses the password value when making a GET call.
We then successfully get a token and we print the body to the console. This works.
However, I now have the challenge of passing the value of body which is my token, to another method for later consumption. printBodyValue currently fails and fails with an 'undefined' error.
How can I pass the value from deep inside getToken to printBodyValue
getToken: function() {
module.exports.readCredentialPassword()
.then(result => {
var request = require('request-promise');
var passwd = result;
var basicAuthData = "Basic " + (new Buffer("fooUser" + ":" + passwd).toString("base64"));
var options = {
method: "GET",
uri: ("http://localhost:8001/service/verify"),
followRedirects: true,
headers: {
"Authorization": basicAuthData
}
};
return request(options)
.then(function (body) {
console.log("Token value is: ", body);
return body;
})
.catch(function (err) {
console.log("Oops! ", err);
});
});
}
printBodyValue: function() {
module.exports.getToken().then(function(body) {
console.log("Token value from printBodyValue is: \n", body);
});
}
In getToken, instead of using the nested promise anti-pattern, chain your promises instead, and return the final promise, so that you can then consume the promise and use its resolved value:
(also, since you're using ES6, prefer const over var)
getToken: function() {
return module.exports.readCredentialPassword()
.then(result => {
const request = require('request-promise');
const passwd = result;
const basicAuthData = "Basic " + (new Buffer("fooUser" + ":" + passwd).toString("base64"));
module.exports.log("Sending Request: ", jenkinsCrumbURL);
const options = {
method: "GET",
uri: ("http://localhost:8001/service/verify"),
followRedirects: true,
headers: {
"Authorization": basicAuthData
}
};
return request(options);
})
.then(function(body) {
console.log("Token value is: ", body);
// the return value below
// will be the final result of the resolution of
// `module.exports.readCredentialPassword`, barring errors:
return body;
})
.catch(function(err) {
console.log("Oops! ", err);
});
}
printBodyValue: function() {
module.exports.getToken().then(function(body) {
console.log("Token value from printBodyValue is: \n", body);
});
}

Categories

Resources