Calling https.get using nodejs from a function? - node.js

I am trying to write my first Alexa Skill and I am doing an http request which works fine, however I would like to wrap this into a function that I can call from my main code.
It is not working and I am not quite sure how I can store the response into a variable, here is my code so far:
function getValue(loc) {
var endpoint = 'URLHERE'
var something = ""
var body = ""
https.get(endpoint, (response) => {
response.on('data', (chunk) => {
body += chunk
})
response.on('end', () => {
data = JSON.parse(body)
something = data.result.node.value;
})
})
return something;
}
This is on amazon and using lambda for the functions using node.js
which I call getValue('test') using
var result = getValue('test')
it just returned undefined.
Any idea's? Thanks
I imagine because its an asynchronous call its not setting the value due to callback but I have tried implementing this and cannot get it to work.
Thanks

You're returning from your function before the callback of http.get, mixing Synchronous with Asynchronous behaviour.
So your function is returning something which hasn't been defined yet.
Try this instead :
function getValue(loc,cb) {
let endpoint = 'URLHERE'
let something = ""
let body = ""
https.get(endpoint, (response) => {
response.on('data', (chunk) => {
body += chunk
})
response.on('end', () => {
data = JSON.parse(body)
cb(data.result.node.value);
})
})
}
getValue(test,(result)=>{
//do something with result here;
});

Related

Node js extract data from a nested function

I am trying to process signup data for a uni project . I am using basic koa modules and I am not allowed to use express, ideally I want to get the data inside the variable post. I want to process the data for example to see if the password has less than 5 characters , if so i would like that the program would not redirect the user to different address but if no errors occur i would like the program to redirect to regOk.html, I tried many other ways like initializing the variable outside of ctx.req.on but none were successful . Can anyone help me ?
export async function postregister(ctx) {
let bodyString = "";
ctx.req.on("data", (chunk) => {
bodyString += chunk;
});
//let collectData = new Array();
ctx.req.on("end", () => {
var post = querystring.parse(bodyString);
var email = post["email"];
var password = post["password"];
var passbestätigen = post["passwort bestä"];
var vorname = post["vorname"];
var nachname = post["nachname"];
var adresse = post["adresse"];
var stadt = post["stadt"];
var telefonnummer = post["telefonnummer"];
var geburtsdatum = post["geburtsdatum"];
var regData = model.add(ctx.db, post);
regData.then(() => console.log("singup successful"))
});
await ctx.render("regOk.html");
}
I'm not very familiar with koa, but I believe your issue is related to the order in which your code is executed.
The event in charge of parsing the data received in the body of the request ends after the synchronic execution of your postregister method, so you never get to see the value of post in the order you'd expect.
One possible solution to go around this issue would be wrapping the parsing of data in a promise, waiting for that promise to complete, and executing then and catch functions once the processing is done.
export async function postregister(ctx) {
await new Promise((resolve) => {
let bodyString = "";
ctx.req.on("data", (chunk) => {
bodyString += chunk;
});
ctx.req.on("end", async () => {
resolve(querystring.parse(bodyString));
});
})
.then(async (post) => {
await model.add(ctx.db, post)
.then(async () => {
console.log("singup successful");
await ctx.render('regOk.html');
});
})
.catch(async (error) => {
console.error(error);
await ctx.render('error.html');
});
}
This way, you handle body parsing inside the Promise, and after that completed you get the result of querystring.parse(bodyString) as a variable named post in your then handler.

Can't add key from function to dictionary

My code:
var price = {};
function getPrice(price) {
const https = require('https');
var item = ('M4A1-S | Decimator (Field-Tested)')
var body = '';
var price = {};
https.get('https://steamcommunity.com/market/priceoverview/?appid=730&market_hash_name=' + item, res => {
res.on('data', data => {
body += data;
})
res.on('end', () => price ['value'] = parseFloat(JSON.parse(body).median_price.substr(1))); //doesnt add to dict
}).on('error', error => console.error(error.message));
}
price['test'] = "123" //adds to dict fine
getPrice(price)
console.log(price);
Output:
{ test: '123' }
as you can see, the "test: 123" gets added, but the "value: xxx" from the function doesn't. Why is that?
There are two main problems here:
You're redeclaring the variable inside your function so you're declaring a separate, new variable and modifying that so the higher scoped variable, never gets your .value property.
You're assigning the property inside an asynchronous callback that runs sometime later after your function has returned and thus your function actually returns and you do the console.log() too soon before you have even obtained the value. This is a classic issue with return asynchronously obtained data from a function in Javascript. You will need to communicate back that data with a callback or with a promise.
I would also suggest that you use a higher level library that supports promises for getting your http request and parsing the results. There are many that already support promises, already read the whole response, already offer JSON parsing built-in, do appropriate error detection and propagation, etc... You don't need to write all that yourself. My favorite library for this is got(), but you can see a list of many good choices here. I would strongly advise that you use promises to communicate back your asynchronous result.
My suggestion for fixing this would be this code:
const got = require('got');
async function getPrice() {
const item = 'M4A1-S | Decimator (Field-Tested)';
const url = 'https://steamcommunity.com/market/priceoverview/?appid=730&market_hash_name=' + item;
const body = await got(url).json();
if (!body.success || !body.median_price) {
throw new Error('Could not obtain price');
}
return parseFloat(body.median_price.substr(1));
}
getPrice().then(value => {
// use value here
console.log(value);
}).catch(err => {
console.log(err);
});
When I run this, it logs 5.2.
You're actually console.logging .price before you're setting .value; .value isn't set until the asynchronous call fires.
You are declaring price again inside the function and also not waiting for the asynchronous task to finish.
const https = require("https");
const getPrice = () =>
new Promise((resolve, reject) => {
const item = "M4A1-S | Decimator (Field-Tested)";
let body = "";
return https
.get(
`https://steamcommunity.com/market/priceoverview/?appid=730&market_hash_name=${item}`,
res => {
res.on("data", data => {
body += data;
});
res.on("end", () =>
resolve(
parseFloat(JSON.parse(body).median_price.substr(1))
)
);
}
)
.on("error", error => reject(error));
});
const main = async () => {
try{
const price = await getPrice();
//use the price value to do something
}catch(error){
console.error(error);
}
};
main();

Getting prefer-arrow-callback with Node https

I saw the the request library was depreciated, so I have been trying to switch to Node's https method instead. I pieced together this basic request function so far.
const https = require('https')
function httpRequest(options) {
return new Promise((resolve, reject) => {
const serverRequest = https.request(options, response => {
let body = ''
response.on('data', function (d) {
body += d
});
response.on('end', function () {
resolve(JSON.parse(body))
})
})
serverRequest.on('error', err => {
reject(err)
})
serverRequest.end()
})
}
It works, but causes eslint to throw prefer-arrow-callback. I don't fully understand why https uses the .on syntax in the first place, so I'm wondering if this function can be re-written in a way that gets rid of the warning and is more in line with modern JavaScript.
I believe that that error means to say it would prefer a Lambda function definition. If you are new to lambda functions, they are formatted as such:
(parameters) => {
}
Try re-writing your code like this:
response.on('data', (d) => {
body += d;
});
response.on('end', () => {
resolve(JSON.parse(body));
});
As for the use of .on, its just how Node formats event listeners.

Cannot access value from function node.js

I am trying to access what a function returns in node.js
I have the following function:
function getImg(callback) {
https.get('https://api.nasa.gov/planetary/apod?api_key=api-key', response => {
let data = "";
response.on('data', chunk => {
data += chunk;
});
response.on('end', () => {
let img = JSON.parse(data).hdurl;
callback(null, img);
})
}).end();
}
let image = getImg(function(err, image) {
console.log(image);
})
res.render('index', {
indexCSS: true,
image
})
It can log it to the console correctly, but if I want to access the value of the variable like I do in the last line of my code or if I console.log(image) I get undefined.
What have I done wrong. How can I access what the function produces?
It is a callback style function which wouldn't return any thing. Better to convert it to promise sttyle and use async/await to get the value in a variable
function getImg() {
return new Promise((resolve, reject) => {
https.get('https://api.nasa.gov/planetary/apod?api_key=api-key', response => {
let data = "";
response.on('data', chunk => {
data += chunk;
});
response.on('end', () => {
let img = JSON.parse(data).hdurl;
resolve(img);
})
}).end();
});
}
(async() => {
let image = await getImg();
res.render('index', {
indexCSS: true,
image
});
})();
You can't really store the return value of your function like that. Unfortunately JS is non-blocking, so the code will continue to execute past it, before it has a chance to return that value from the https request. I am not sure exactly when you call this function, but you could call res.render in the callbacks response after calling getImg() without assigning its value to something. You can use promises, otherwise it's better to handle the response you need when it is returned from the callback. That would just be a simple call like:
getImg(function(err, image) {
res.render('index', {
indexCSS: true,
image
});
})
Within whatever route is calling this function. You just cannot assign any kind of return value from a callback to a variable (really not recommended at least) in the normal way.

AWS nodejs lambda persist output of asynchronous call in JavaScript object

It's a silly question perhaps, but I spent quite sometime trying to resolve but not able to get the data.
In the below function checkMailCount, I have an object called test with a function in it to assign the value to mails. The value assigned from res.on('end') to this test.mails via mailCount is not retained/persisting.
'use strict';
const https = require('https');
exports.handler = (event, context, callback) => {
let session = event.session;
let request = event.request;
if (request.type === "something") {
if (request.name === "blah blah") {
let emailCount = checkMailCount(session, callback);
context.succeed(callback(null, emailCount));
}
}
function checkMailCount(session, callback) {
let url = 'https://www.googleapis.com/gmail/v1/users/me/messages?
access_token = $ {
session.user.accesstoken
} & q = "is:unread"
';
let test = {
mails: "",
mailCount: function(val) {
this.mails = val;
}
};
let data = "";
https.get(url, function(res) {
res.on('data', function(chunk) {
data += chunk;
//console.log(data);
});
res.on('end', function() {
let result = JSON.parse(data);
//result2+= result.replace('/\//g','');
test.mailCount(result["resultSizeEstimate"]);
// result["resultSizeEstimate"] inaccessible outside
});
});
return test.mails; // This is returning undefined
}
//context.fail();
}
Below is the response JSON from gmail API
{
"messages": [
{
"id": "165f1627a53f70c6",
"threadId": "165f000031cee15b"
},
{
"id": "165f00d9e2e07737",
"threadId": "165f00d9e2e07237"
}
],
"nextPageToken": "10349401829330188334",
"resultSizeEstimate": 2
}
I need to return the value of result["resultSizeEstimate"] from the function checkMailCount. The value of result, result["resultSizeEstimate"] is available inside res.on('end', f(){It is available here}) but not outside.
I tried this in VS code not as lambda, I was able to do it by other means. Its sort of hard to unit test lambda functions. Is there a way, I can send the email count to context.succeed(callback(null, emailCount)) ?
It seems like there's a fundamental issue with callbacks in this code. It is not possible for checkMailCount to behave like you want it to in its current form. checkMailCount is asynchronous. JavaScript has a few different ways to deal with asynchronous code. The oldest is what you're doing here, callbacks. Next is promises and finally async/await. This code is probably easier to write if you switch to async/await. That said, if you want to keep using the callback pattern you're gonna have to ya know, use them.
Instead of passing lambda's callback to checkMailCount you should pass your own.
checkMailCount(session, function(err, result) {
// result === result.resultSizeEstimate
// Call your lambda callback from within this function
});
res.on('end', function() {
let result = JSON.parse(data);
test.mailCount(result["resultSizeEstimate"]);
callback(null, result.resultSizeEstimate)
});

Resources