Node.js Http.Request Callback - unable to pass data to parent function - javascript

I'm fairly new to Node and Javascript and I'm struggling with my first Node module. What I'm trying to do is export functions for specific API calls and I'd like to reuse my https.request function rather than duplicating the code in each function. For some reason I'm failing to wrap my head around how to pass the data back to my original function. Here's an abbreviated version - the listStuff function would be one of many to handle various api request actions.
'use strict';
const https = require('https');
const _ = require('underscore');
const hostStr = 'api.server.net';
function listStuff(){
var pathStr = '/release/api/stuff';
_apiCall(pathStr);
//Would like to handle the https response data here
};
function _apiCall(pathStr){
var options = {
host: hostStr,
path: pathStr
};
var req = https.get(options, function(res) {
console.log("statusCode: ", res.statusCode);
console.log("headers: ", res.headers);
var responseString = '';
res.on('data', function(d){
responseString += d;
});
res.on('end', function(){
var responseObject = JSON.parse(responseString);
});
});
req.end();
req.on('error', function(e){
console.log(e);
});
};
module.exports = {
listStuff: listStuff
};

Hope this helps. Register a callback in the apiCall function, and then check the callback params for error handling. Then, just make sure you return the callback when you want to end the function call (either in the on end or on error processing).
function listStuff(){
var pathStr = '/release/api/stuff';
_apiCall(pathStr, function(err, data) {
if (err) // handle err
//handle data.
});
};
function _apiCall(pathStr, callback){
var options = {
host: hostStr,
path: pathStr
};
var req = https.get(options, function(res) {
console.log("statusCode: ", res.statusCode);
console.log("headers: ", res.headers);
var responseString = '';
res.on('data', function(d){
responseString += d;
});
res.on('end', function(){
var responseObject = JSON.parse(responseString);
return callback(null, responseObject);
});
});
req.end();
req.on('error', function(e){
console.log(e);
return callback(e);
});
};

A slightly different approach using Promise objects. Note I looked into this as a learning exercise and hope it helps. I have not written all the code for you and the debugging is all yours!
Firstly make _apiCall returns a promise object.
function listStuff()
{
var pathStr = '/release/api/stuff';
var promise = _apiCall(pathStr);
promise.then( function( responceObject){
// handle response object data
});
promise.catch( function( error){
console.log( error.message); // report error
});
}
Next step is to make _apiCall return a promise object for the HTTPS request it will initiate inside the executor of promise creation.
function _apiCall(pathStr)
{ var options = {
host: hostStr,
path: pathStr
};
function beginGet( worked, failed)
{
// see below
}
return new Promise( beginGet);
}
Lastly write beginGet to initiate and call back success or fail functions depending on the outcome of the get request.
function beginGet( worked, failed)
{ var req;
var responseObj;
function getCallBack( res)
{ // all your get request handling code
// on error call failed( error)
// on sucessful completion, call worked(responseObj)
}
req = https.get(options, getCallBack);
}
Also please check with https.get documentation - I think it calls req.end() for you. All the other errors are mine :-)

Related

How can I redirect to URL after variable value is loaded?

I have an application in Node with Express and trying to successfully finish the implementation of a payment system called "Flow".
[EDIT]
I bypassed something very important, it's a function that wraps the functionality that is triggering after the end of the method. I tried making it async so I can wrap the functions then as a promise like someone adviced, but got some errors. I'll re-check.
//REALIZAR PAGO $
app.post('/realizarPago', function(req, res){
var amount = Math.floor(Math.random() * 999999) + 100000;
var paymentMethod = 9;
var urlRedirect = "https://sandbox.flow.cl/app/web/pay.php?token=";
...
//Other relevant variables
var req = https.request(options, function (res) { //<=== This one
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
...
//this is just a sample token that I'll later capture from response
urlRedirect = urlRedirect+"1234567890";
});
});
var postData = qs.stringify({
'flowOrder': flowOrder,
'amount': amount,
...
});
req.write(postData);
res.redirect(307, urlRedirect);
req.end();
});
Everything works almost fine, the problem is that the redirect is executed before urlRedirect updates its value in urlRedirect = urlRedirect+"1234567890";
I've tried with .then() after res.write(postData) executes, with no useful result. I've tried .then() in almost every method, I've also tried with res instead of req and viceversa. I also did some research and found an interesting thing called "waterfall" for async methods but in case of res.on methods can't imagine a way of encapsulating this.
What can I try next?
[EDIT 2]
Thanks to all of your guidance, I was able to use some promises and change the execution order. Now it's doing what I need, now it says that some parameters are missing in the https request. I'll have to fix that. The final code is:
//REALIZAR PAGO $
app.post('/realizarPago', async function(request, response){
var amount = Math.floor(Math.random() * 999999) + 100000;
var paymentMethod = 9;
...
//Other relevant variables
var urlRedirect = "https://sandbox.flow.cl/app/web/pay.php?token=";
var req = https.request(options, (res) => {
var chunks = [];
res.on("data", (chunk) => {
chunks.push(chunk);
});
res.on("end", (chunk) => {
var body = Buffer.concat(chunks);
var datos = body.toString();
datos = JSON.parse(datos);
var token = datos.token;
urlRedirect = urlRedirect+token
response.redirect(urlRedirect);
});
});
req.end();
var postData = qs.stringify({
'flowOrder': flowOrder,
'amount': amount,
...
});
req.write(postData);
req.end();
});
You put the redirect into the end event handler, and you also add an error handler in case the request fails and you change the name of one of the two res arguments so they don't conflict and you can still access both of them. I changed the higher level one to response so it can be used inside the other callbacks where res is already defined:
//REALIZAR PAGO $
app.post('/realizarPago', function(req, response){ // <== change arg name to response
var amount = Math.floor(Math.random() * 999999) + 100000;
var paymentMethod = 9;
var urlRedirect = "https://sandbox.flow.cl/app/web/pay.php?token=";
...
//Other relevant variables
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
...
//this is just a sample token that I'll later capture from response
urlRedirect += "1234567890";
response.redirect(307, urlRedirect); // <== move this here
});
});
req.on('error', err => { // <== add this
console.log(err);
response.sendStatus(500);
});
var postData = qs.stringify({
'flowOrder': flowOrder,
'amount': amount,
...
});
req.write(postData);
req.end();
});

Client request to server

Client function is requesting for data from Server. I imported this function in other file where I need it to use. It is working fine. But i don't understand why I am receiving on command prompt "Undefined". I have commented all console.log but still it is coming. I'm not sure if export/import has problem?
Here is my code:
// tstReq.js
function getData(iccid) {
toString(iccid);
var http = require('http');
var jasParseData;
var options = {
host: 'demo8620001.mockable.io',
port: 80,
path: '/Api3',
method: 'get'
};
http.request(options, function(res) {
//console.log('STATUS: ' + res.statusCode);
//console.log('HEADERS: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function (chunk) {
//console.log('BODY: ' + chunk);
josParseData= JSON.parse(chunk);
for(i = 0, len = Object.keys(josParseData.iccid[i]).length; i<=len; i++) {
//console.log('JSON.parse:',josParseData.iccid[i]);
//console.log("iccid: ",iccid);
if (josParseData.iccid[i] === iccid) { // Only printed match iccid
console.log('JSON.parse:',josParseData.iccid[i]);
console.log("iccid: ",iccid);
}
if (josParseData.iccid[i] === iccid) {
console.log("Valid Jasper", i+1);
console.log('\n');
}
else{
// console.log ("Invlid Jasper");
}
//console.log('\n');
}
//console.log('\n');
});
}).end();
};
module.exports = getData;
Here is code where I am using exported function:
const fs = require('fs');
var parse = require('csv-parse');
var validateICCID = require('./funcValidateId.js');
var getData = require('./tstReq.js');
fs.createReadStream('iccid2.csv')
.pipe(parse({delimiter: ':'}))
.on('data',function(csv) {
csvrow= csv.toString();
console.log('\n');
console.log(getData(csvrow));
console.log('\n');
});
You're probably getting undefined because the function getData doesn't have a return statement. It doesn't mean your function is broken, it's just not directly returning a result.
If you want to log the result of getData, you'll need to return http.request, and you'll need to resolve the http.request by returning in the callback.
Also, I noticed you declared var jasParseData you probably meant josParseData.

Asynchronous function call with status variable on NodeJS and Javascript

I'm writing a REST client to access a REST server that paginates the replies. I've written something like the below, using Node's HTTPS library:
var nextPage = true, pageNo=0;
do {
// builds the url
options.path = "?page=" + pageNo;
var req = HTTPS.request(options, function(res) {
res.on('data', function (chunk) {
parser(chunk, function(err, result) {
// do something with result
if (!("nextPage" in result)) {
nextPage = false;
}
});
});
});
// handle errors and end request
pageNo++;
} while(nextPage)
This of course doesn't work, because the request to the server is handled asynchronously and the nextPage variable is never updated, but I can't figure out a way to make it work using higher-order functions.
Should I wrap the request around a recursive function, passing the state every time? Or is there a better way?
UPDATE 1:
Do you mean something like this?
var nextPage = true, pageNo=0;
var async = require('async');
async.doWhilst ( function () {
// builds the url
options.path = "?page=" + pageNo;
var req = HTTPS.request(options, function(res) {...});
// handle errors and end request
pageNo++;
}, function () {return nextPage;}, function () {});
It does not work either, what am I doing wrong?

With node.js how to I set a var to response from HTTP client?

I know this is probably Asynchronous Javascript 101 and I do have some books on the Kindle I could consult, but I am nowhere near my device.
I have a node app with a variable being assigned to a module that I'm loading. The module has one function that downloads a string of data from a URL.
The problem is, how do I not set the variable until the request has returned?
My code looks like this:
Downloader.js:
var http = require('http');
exports.downloadString = function(str) {
console.log("Downloading from " + str);
http.get(str, function(res) {
var data = [];
console.log("Got response: " + res.statusCode);
res.on('data', function (chunk) {
data.push(chunk);
});
res.on('end', function() {
return data.join('');
});
}).on('error', function(e) {
console.log("Got error: " + e.message);
});
}
app.js:
var downloader = require('./lib/Downloader')
, dateParser = require('./lib/DateParser')
, eventIdGenerator = require('./lib/EventIdGenerator');
var report = downloader.downloadString("http://exampleapi.com");
console.log(report);
I need to wait until the variable named "report" is populated.
Obviously this means my Mocha tests are also failing as I'm still unsure of how to tell the test to wait until the variable is filled.
I'm sure this is extremely simple, but I am drawing a blank!
Thanks!
Node.js is (mostly) asynchronous, so you'd need to pass a callback function to your module:
Downloader.js:
var http = require('http');
exports.downloadString = function(str, callback) {
console.log("Downloading from " + str);
http.get(str, function(res) {
var data = [];
console.log("Got response: " + res.statusCode);
res.on('data', function (chunk) {
data.push(chunk);
});
res.on('end', function() {
callback(data.join(''));
});
}).on('error', function(e) {
console.log("Got error: " + e.message);
});
};
app.js:
var downloader = require('./lib/Downloader')
, dateParser = require('./lib/DateParser')
, eventIdGenerator = require('./lib/EventIdGenerator');
downloader.downloadString("http://exampleapi.com", function(report) {
console.log(report);
});

Node.js http.get as a function

I am trying to make a function that returns the content of the webpage and this is what I have so far
var get_contents = function() {
var httpRequestParams =
{
host: "google.com",
port: 80,
path: "/?"
};
var req = http.get(httpRequestParams, function(res)
{
var data = '';
res.on('data', function(chunk) {
data += chunk.toString();
});
//console.log(data);
}).end();
return req;
}
This when I run this code, I see the html contents when the console logging is turned on but when I try to return the output, it just never works.
I can't figure out a way to return get_contents() anywhere. On the console, it just doesnt respond.
Thanks
Something like that: (dont forget to handle error and timeout)
var on_contents = function(cb) {
var httpRequestParams =
{
host: "google.com",
port: 80,
path: "/?"
};
var req = http.get(httpRequestParams, function(res)
{
var data = '';
res.on('data', function(chunk) {
data += chunk.toString();
});
res.on('end', function(){
cb(data);
});
//console.log(data);
}).end();
}
function onFinish(data) {
console.log(data);
}
on_contents(onFinish)
The short answer is: You can't return the data from that function. http.get is asynchronous, so it doesn't actually start running the callback until after your function ends. You'll need to have your get_contents function take a callback itself, check in the http.get handler whether you're done loading and, if you are, call the get_contents callback.
There is an awesome module [request][1] available in node.js.
var request = require('request'),
url = require('url');
var server = http.createServer(function (request, response) {
getPage("http://isohunt.com/torrents/?iht=-1&ihq=life+is+beautiful", function (body) {
console.log(body);
})
});
server.listen(3000);
More information can be found on http://www.catonmat.net/blog/nodejs-modules-request/

Categories

Resources