Trying to use HTTPS Get request [Node.Js] for rest of project - node.js

I am trying to use an API to get IP address that gets a local city. Then using the city name from the IP address to run threw another API to get the local weather when they visit my cite. This is my first NodeJs project I am attempting on my own. I wanted one API call to get a JSON object that contains the info of the IP address. So far I have only gotten undefined or Promise {pending}.
I have looked up using async/await functions and using the fetch() function instead of https.
I have also tried wrapping the get request around a new Promise function but still with undefined results. In this project I have the main app.js and a functions.js in attempt to keep clean code. I am completely lost.
first try.
function getRawData(){
const https = require('https');
return new Promise((resolve, reject) =>{
https.get('https://api.ipgeolocation.io/ipgeo?apiKey=' + ipApiKey, res => {
console.log('statusCode:', res.statusCode);
res.on('data', d => {
let data = JSON.parse(d);
let cData = data.city
resolve(cData);
}).on('error', e =>{
reject('error'. e.message);
});
});
});
}
function getIp(){
getRawData().then(data =>{
return data;
}).catch(error => {
console.log(error);
});
}
A different type of code I tried:
async function getIPAddress(){
let data = await fetch('https://api.ipgeolocation.io/ipgeo?apiKey=' + ipApiKey)
.then(res => res.json())
.then(res => {return res});
return data;
}
when I would call any of the functions on the main app.js I would not get the JSON data I need. Please help.

I found my answer using the fetch function in a stacked call.
function requestIP(url){
return fetch(url)
.then(r => r.json())
.then(json => json);
}
fs.requestIP(ipUrl).then(data =>{
const part ='hourly,alerts,minutely';
const lat = data.lat;
const lon = data.lon;
const name = data.city;
const apiKey = process.env.WEATHER_API_KEY;
const units = "imperial";
const urlWeather = 'https://api.openweathermap.org/data/2.5/onecall?lat=' + lat + '&lon=' + lon + '&units=' + units + '&exclude=' + part + '&appid=' + apiKey;
fetch(urlWeather)
.then(r => r.json())
.then(json =>{
let todayW = json.current;
let tomW = json.daily[0];
let n1Day = json.daily[1];
let n2Day = json.daily[2];
let todayDate = fs.todaysDate();
res.render('home', {
CR: fs.getCopyRights(),
dayDate: todayDate,
cityName: name,
todayT: Math.floor(todayW.temp),
todayI: todayW.weather[0].icon,
tomT: Math.floor(tomW.temp.day),
tomI: tomW.weather[0].icon,
next1T: Math.floor(n1Day.temp.day),
next1I: n1Day.weather[0].icon,
next2T: Math.floor(n2Day.temp.day),
next2I: n2Day.weather[0].icon,
});
});
});

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();

IBM Action not returning anything after GET request

First of all, I am a beginner in Javascript, so if there are any uncertainities or unclarities in my message, please feel free to correct me.
I try to create an action to support my IBM Watson Assistant. Once called, the action should get some info from a http and give some answer back.
The "get" action was part of a Webhook, successfully deployed via Heroku as support for Dialogflow. I just changed it a little bit, to make the answer easier.
function main(req){
const http = require('http');
const API_KEY = '85324cac';
const prodname = req.prodname;
const reqUrl = encodeURI(`http://www.omdbapi.com/?t=${prodname}&apikey=${API_KEY}`);
http.get(reqUrl, (responseFromAPI) => {
let completeResponse = '';
responseFromAPI.on('data', (chunk) => {
completeResponse += chunk;
});
responseFromAPI.on('end', () => {
const movie = JSON.parse(completeResponse);
let dataToSend = prodname ;
dataToSend += (typeof movie.Title === "undefined") ? `Sorry the film is not in our database` : `${movie.Title} is a ${movie.Actors} starer ${movie.Genre} movie, released in ${movie.Year}. It was directed by ${movie.Director}.`;
return {answer: dataToSend};
});
});
//return {answer: dataToSend};
}
I was expecting an answer after the "return" action, but it is only showing empty values. I am pretty sure that the action does never get into the "http.get" part. When I remove the // and I invoke the code, it returns the following message: "dataToSend is not defined"; if I keep code as it is (with the comment), no errors pop up.
The omdapi is for free, but hosted in the US, in case that could matter.
Any ideas? In any case, thanks in advance.
Think you will find that your ibm function is completing before your external call to omdbapi is returning. Your best choice here is to use promises ( being new I expect you may not have used promises yet - would recommend reading https://cloud.ibm.com/docs/openwhisk?topic=cloud-functions-creating-javascript-actions#creating-javascript-actions
Not your complete program, leave you something to play with;
function main(req){
const http = require('http');
const API_KEY = '85324ca';
//const prodname = req.prodname;
const prodname = 'Game%20of%20Thrones&Season=1';
const reqUrl = 'http://www.omdbapi.com/?t=Game%20of%20Thrones&Season=1&apikey=85324cac';
//const reqUrl = encodeURI(`http://www.omdbapi.com/?t=${prodname}&apikey=${API_KEY}`);
return new Promise(function(resolve, reject) {
http.get(reqUrl, (responseFromAPI) => {
let completeResponse = '';
responseFromAPI.on('data', (chunk) => {
completeResponse += chunk;
// you could return answer here via resolve.
//var parsedData = JSON.parse(completeResponse);
//console.log(parsedData);
//resolve(parsedData);
})
responseFromAPI.on('error', (error) => {
console.log(error);
reject(error);
})
responseFromAPI.on('end', () => {
var parsedData = JSON.parse(completeResponse);
console.log(parsedData);
resolve(parsedData);
});
});
});
}

How to write native Nodejs async https request code

I have copied the very good code from https://www.tomas-dvorak.cz/posts/nodejs-request-without-dependencies/ to make a http request in nodejs using native modules.
I want to be able to use the data value later on in the script.
I know this is a common issue with newbies and async code, i just CANNOT understand this yet and have struggled for weeks to get it.
I have coped much code, watched youtube, talked to people, its flippen hard..
const getContent = function(url) {
return new Promise((resolve, reject) => {
const https = require('https')
const request = https.get(url, (response) => {
// handle http errors
if (response.statusCode < 200 || response.statusCode > 299) {
reject(new Error('Failed to load page, status code: ' + response.statusCode));
}
// temporary data holder
const body = [];
// on every content chunk, push it to the data array
response.on('data', (chunk) => body.push(chunk));
// we are done, resolve promise with those joined chunks
response.on('end', () => resolve(body.join('')));
});
// handle connection errors of the request
request.on('error', (err) => reject(err))
})
}
getContent('https://myapi/json')
.then((data) => console.log(data))
.catch((err) => console.error(err))
// I want to use the "data" value down here in my script. I want to do things with the "data" value like JSON.parse(data)
console.log(data) //undefined
let json = JSON.parse(data) //undefined
console.log('after')
my result for data is undefined
How can i use data down here below all the code above?
You can setup a callback and access your data within this callback, this pattern should be easy enough to use.
getContent('https://myapi/json')
.then(useData)
.catch((err) => console.error(err))
// Use this callback to do what you want with your data!
function useData(data) {
console.log(data);
let json = JSON.parse(data);
}
Or using async / await ( this might be more intuitive!):
async function testAwait() {
let data = await getContent('https://myapi/json');
console.log("data: ", data);
}
testAwait();

Axios.all, how to configure axios wait time to mitigate hung up?

My application uses an internal webservice for fetching data, i have a job which creates approx 500 requests which getsfired async to complete the fetch operation.
I make use of Axios, by creating an array of axios promises and then resolving them using using Axios.all();
It works fine until some 200 requests but post that i get socket hung up, however on the server side i see the requests are being processed.
How to configure axios to set custom time out, or is it a better idea to splice my promises array and then run them as multiple batches ?
Source code
let getAxiosPromiseArray = (urlList) => {
var axiosArrayofPromise = [];
return new Promise ( (resolve, reject) => {
try {
urlList.forEach ( (URL) => {
axiosArrayofPromise.push(axios.get(URL));
});
resolve(axiosArrayofPromise);
}
catch (err) {
reject("There is a problem getting Axios array of promises " + err);
}
})
}
async function processAxiosPromises (PromiseArray) {
try {
var results = []
results = await axios.all(PromiseArray);
return results;
}
catch(err) {
throw("There was a problem resolving promises array (Axios) " + err);
}
}
getallID().then ( (urlList) => {
return getAxiosPromiseArray(urlList);
}).then( (AxiosPromises) => {
return processAxiosPromises(AxiosPromises);
}).then ((resultData) => {
console.log(resultData);
});
Error
There was a problem resolving promises array (Axios) Error: socket hang up
First, that pair of functions getAxiosPromiseArray() and processAxiosPromises() needs fixing.
Your new Promise() construction is unnecessary. You can simply return Promise.all(arrayofPromise) (or axios.all(...) if you must) and do away with the other function.
Renaming the remaining function to something meaningful, you would end up with eg :
let getData = (urlList) => {
return Promise.all(urlList.map(URL => axios.get(URL)))
.catch(error => {
error.message = "There is a problem getting Axios array of promises " + error.message; // augment the error message ...
throw error; // ... and re-throw the errror.
});
};
And call as follows :
getallID().then(getData)
.then(resultData => {
console.log(resultData);
}).catch(error => {
console.error(error);
});
That will put you on solid ground but, on its own, is unlikely to fix a concurrency problem (if that's what it is), for which the simplest approach is to use Bluebird's Promise.map with the concurrency option.
The caller code can remain the same, just change getData(), as follows:
let getData = (urlList) => {
let concurrency = 10; // play with this value to find a reliable concurrency limit
return Promise.map(urlList, URL => axios.get(URL), {'concurrency': concurrency})
.catch(error => {
error.message = "There is a problem getting Axios array of promises " + error.message;
throw error;
});
};
// where `Promise` is Bluebird.
const axios = require('axios');
const axiosThrottle = require('axios-throttle');
//pass axios object and value of the delay between requests in ms
axiosThrottle.init(axios,200)
const options = {
method: 'GET',
};
const urlList = [
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3',
'https://jsonplaceholder.typicode.com/todos/4',
'https://jsonplaceholder.typicode.com/todos/5',
'https://jsonplaceholder.typicode.com/todos/6',
'https://jsonplaceholder.typicode.com/todos/7',
'https://jsonplaceholder.typicode.com/todos/8',
'https://jsonplaceholder.typicode.com/todos/9',
'https://jsonplaceholder.typicode.com/todos/10'
];
const promises = [];
const responseInterceptor = response => {
console.log(response.data);
return response;
};
//add interceptor to work with each response seperately when it is resolved
axios.interceptors.response.use(responseInterceptor, error => {
return Promise.reject(error);
});
for (let index = 0; index < urlList.length; index++) {
options.url = urlList[index];
promises.push(axiosThrottle.getRequestPromise(options, index));
}
//run when all promises are resolved
axios.all(promises).then(responses => {
console.log(responses.length);
});
https://github.com/arekgotfryd/axios-throttle

Resources