simple GET request in mocha timeout - javascript

I have the following code
const https = require("https");
it("wait for some result", function (done) {
this.timeout(15000);
const options = {
hostname: "httpbin.org",
path: "/get",
headers: {
Authorization: "bearer ",
},
};
https.get(options, (resp) => {
let data = "";
// A chunk of data has been recieved.
resp.on("data", (chunk) => {
data += chunk;
console.log(data);
});
// The whole response has been received. Print out the result.
resp.on("end", () => {
console.log(JSON.parse(data).explanation);
});
resp.on("error", (err) => {
console.log("Error: " + err.message);
});
done();
});
});
this returns:
should respond with redirect on post:
Error: timeout of 15000ms exceeded. Ensure the done() callback is being called in this test.
I am trying to make this asynchronous because of mocha, which is why I am using callback. I am even waiting 15000 instead of the default 2000ms.
this code normally works, only fails with mocha.
Not sure how to fix this issue. Any help is appreciated.
Thanks in advance.

try to add .end() on your request object
const req = https.get(options, (resp) => {
let data = "";
// A chunk of data has been recieved.
resp.on("data", (chunk) => {
data += chunk;
console.log(data);
});
// The whole response has been received. Print out the result.
resp.on("end", () => {
console.log(JSON.parse(data).explanation);
done();
});
resp.on("error", (err) => {
console.log("Error: " + err.message);
});
});
req.end()

Related

Undersanding request get in node.js

I'm trying to do a GET request in node.js. I would like to access the result of the request but there is a problem. Here is the code :
const https = require('https');
var data;
https.get('https://www.example.com', (resp) => {
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
console.log("Inside : "+data+"\n");
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
console.log("Outside : "+data+"\n");
The outside message appears before the inside one, and only the latter displays the request. That is due to asynchronous process I guess. But if I want to use my request outside, it's a problem because of this delay.
How may I do?
Your code execute asyncronymos. http.get(...) execute, but programm don't wait result of your get. So when you do "console.log("Outside : "+data+"\n");" - your data is empty. You will never get data to outside message, without js constructions like async/await.
This is what you need:
(async function () {
const https = require('https');
var data;
await new Promise((resolve, reject) => {https.get('https://www.example.com', (resp) => {
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
console.log("\n\n===>Inside : "+data+"\n");
resolve();
});
}).on("error", (err) => {
console.log("Error: " + err.message);
reject();
});
});
console.log("\n\n===>Outside : "+data+"\n");
})();
Thanks for answering. After searches, I made this, which work, but I don't know if it's the best way to do this.
const https = require('https');
var data;
(async function () {
var p = new Promise((resolve) => {https.get('https://www.example.com', (resp) => {
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
console.log("\n\n===>Inside : "+data+"\n");
resolve();
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
});
await p;
console.log("\n\n===>Outside : "+data+"\n");
})();

How can i return response only if one methodcall is completed in node js

I have a node js with express application. I need to expose a rest endpoint that will return the response of a http call. Whatever I try it returns before the http request. Can you please help
app.all('/query', function(req, res){
// here i need to make a http call
let urlCall = new Promise((resolve, reject) => {
http.get('http://test.com', (response) => {
let sdata = '';
response.on('data', (fragments) => {
sdata += fragments;
});
response.on('end', () => {
let response_body = sdata;
resolve(response_body.toString());
});
response.on('error', (error) => {
// promise rejected on error
reject(error);
});
});
});
urlCall.then((response) => {
var responseData=response;
res.json(responseData);
res.end();
}).catch((error) => {
console.log(error);
res.end();
});
}
Your code should work, but I suspect a request error not being handled (the error event handler being missing on your request)
You can try moving the error handler from the response to the request
app.all("/query", function (req, res) {
// here i need to make a http call
let urlCall = new Promise((resolve, reject) => {
http
.get("http://test.com", (response) => {
let sdata = "";
response.on("data", (fragments) => {
sdata += fragments;
});
response.on("end", () => {
let response_body = sdata;
resolve(response_body.toString());
});
})
.on("error", (error) => { // handling request errors
console.error("error");
// promise rejected on error
reject(error);
});
});
urlCall.then(
(response) => {
res.json(response);
res.end();
},
(error) => {
console.log(error);
res.end();
}
);
});

Invoking an api inside a aws lambda but getting a null response

I am trying to invoke a rest API inside an API but it is not returning anything. So I am making a simple lambda which returns a JSON but getting a null value as a response.
var https = require('https');
var dt;
exports.handler = async (event, context) => {
var data = '';
return new Promise((resolve, reject) => {
var params = {
host: "cvwtzygw4a.execute-api.ap-south-1.amazonaws.com",
path: "/test/first"
};
const req = https.request(params, (res) => {
console.log('STATUS: ' + res.statusCode);
res.setEncoding('utf8');
res.o n('data', function(chunk) {
data += chunk;
});
res.on('end', function() {
console.log("DONE");
console.log(data);
dt = JSON.parse(data);
console.log(dt);
});
resolve(dt);
});
req.on('error', (e) => {
reject(e.message);
});
// send the request
req.write('');
req.end();
});
};
You should go through this article to understand how to use NodeJs promises in AWS Lambda. In this, the second solution addresses your use case.
To be specific to your code, I modified to make it very simple using the async/await syntax and the request-promise library.
const request = require('request-promise');
exports.handler = async (event, context) => {
var data = '';
try {
data = await request.get('https://cvwtzygw4a.execute-api.ap-south-1.amazonaws.com/test/first');
console.log('response received', res);
} catch (error) {
console.log('Error', error);
}
return data;
};
Following was the output:
START RequestId: 80d75f93-5fa6-1354-c22c-0597beb075e7 Version: $LATEST
2020-01-03T17:51:19.987Z 80d75f93-5fa6-1354-c22c-0597beb075e7 response received {
"basic" : {"name":"John","age":31,"city":"New York"}
}
END RequestId: 80d75f93-5fa6-1354-c22c-0597beb075e7
REPORT RequestId: 80d75f93-5fa6-1354-c22c-0597beb075e7 Init Duration: 907.81 ms Duration: 1258.54 ms Billed Duration: 1300 ms Memory Size: 128 MB Max Memory Used: 55 MB
"{\n\"basic\" : {\"name\":\"John\",\"age\":31,\"city\":\"New York\"}\n}"

node.js async callback error on queue.drain

I am trying to use a callback to indicate when all the async workers are complete, but I am getting the dreaded
TypeError: callback is not a function.
I would like to individually process each element in data, and on completion, have queue.drain to send the callback(data) to refresh Data on completion. I have been readying the async documentation, but clearly i am not getting something.
function refreshData(postData, callback) {
var options = {
host: 'www.myhost.com',
port: 443,
path: '/pulldata,
method: 'POST',
headers: {
"Content-Type": "application/json"
}
};
var req = https.request(options, function(res) {
var headers = res.headers
var d = '';
res.setEncoding('utf8');
res.on('data', function (chunk) {
d = d + chunk;
});
res.on('end', function() {
if (res.statusCode == '200') {
data = JSON.parse(d);
queue = async.queue(function (task, cb) {
processData(task,cb);
},1);
//this is were the errors are
queue.drain = function() {
callback(data)
};
for(i=0; i<data.length; i++) {
queue.push(data[i],'');
}
} else {
callback(false)
}
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
// write data to request body
req.write(postData);
req.end();
}
Any assistance would be greatly appreciated!
Edit, added some pseudo code to demonstrate how refreshData is being used:
Node https.createServer(req,res) {
req.on(){
read userData
}
req.end(){
validateUser(userData, function(callbackData) {
if(callbackData==false) {
//bad user or error with request
res.writeHead(404);
res.end('bye');
} else {
//good user and responses
res.writeHead(200);
res.end(callbackData);
}
})
}
}
function validateUser(userData,callback) {
//do some stuff to validate
if(userData is good) {
//call refreshData if user
refreshData(userData,callback)
} else {
callback(false)
}
}
[EDIT] Added a callback
As given in the documentation you pointed to , change this line
queue.push(data[i],'');
to
queue.push(data[i], function(err){
// handle error
});
Try it here async-queue-callback

Using the results of a GET request in Express router

First Node/Express app.
I'm having a hard time wrapping my head around on how to retrieve data from an endpoint and rendering it in the browser.
I have a dataservice.js that gets a JSON object from an endpoint like this:
const http = require('http');
getFinhockeyData = function() {
http.get('http://tilastopalvelu.fi/ih/modules/mod_standings/helper/standings.php?statgroupid=3545', (res) => {
console.log(`Got response: ${res.statusCode}`);
var body = "";
res.on('data', function (chunk) {
body += chunk;
})
res.on('end', function () {
var data = JSON.parse(body);
console.log('data parsed.');
console.log('first team name: ' + data.teams[0].TeamName);
console.log(typeof data);
return data;
})
}).on('error', (e) => {
console.log(`Got error from Finhockey: ${e.message}`);
});
}
module.exports.getFinhockeyData = getFinhockeyData;
Up until now things work and the data object can be console.logged and its content is usable.
The router.js looks currently like this:
'use strict';
const express = require('express');
const async = require('async');
const router = express.Router();
const dataservice = require('./dataservice.js')
router.get('/', function(req, res) {
async.series([
function(callback) {
getFinhockeyData(callback)
}
],
function(err, results) {
console.log('start rendering');
res.render('index', { data: data });
})
});
module.exports = router;
When I run the app and refresh the / route, I can see from the console that the getFinhockeyData is called and the data object's content is available in dataservice.js's console.logs, but the browser window hangs and the res.render part is never reached.
I understand that the rendering should be done only after the endpoint data request has finished (async.series usage), but it seems that I lack a fundamental understanding on how to actually use the result data from the getFinhockeyData function in the main route.
Any advice on this? I'll be happy to provide more info if necessary.
Firstly, doing the request is asynchronous, so you'll have to use either a callback or a promise.
Even the async middleware won't let you just return data from an asynchronous call, it requires a callback, but using native promises seems easier here
const http = require('http');
getFinhockeyData = function() {
return new Promise( (resolve, reject) => {
http.get('http://tilastopalvelu.fi/ih/modules/mod_standings/helper/standings.php?statgroupid=3545', (res) => {
var body = "";
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
resolve( JSON.parse(body) );
});
}).on('error', reject);
});
}
module.exports.getFinhockeyData = getFinhockeyData;
Also note that you're exporting as a module with a property
module.exports.getFinhockeyData = getFinhockeyData;
when you're going to use that in the routes, you have to use the property
const dataservice = require('./dataservice.js');
router.get('/', function(req, res) {
dataservice.getFinhockeyData().then(function(data) {
res.render('index', { data: JSON.stringify(data) });
}).catch(function(err) {
// epic fail, handle error here
});
});
You are responding to your route call with
res.render('index', { data: data });
But there is no data variable. It should be
res.render('index', { data: results });
Which is the variable where you are storing your data when it comes from the callback
The reason for res.render() not being called is, http requests are async. To get the response a callback must be passed, which you did but forgot to call it in the dataservice.js
This should help...
Change your dataservice.js like the following...
const http = require('http');
getFinhockeyData = function(callback) {
http.get('http://tilastopalvelu.fi/ih/modules/mod_standings/helper/standings.php?statgroupid=3545', (res) => {
console.log(`Got response: ${res.statusCode}`);
var body = "";
res.on('data', function (chunk) {
body += chunk;
})
res.on('end', function () {
var data = JSON.parse(body);
console.log('data parsed.');
console.log('first team name: ' + data.teams[0].TeamName);
console.log(typeof data);
callback(null, data); //returning the data to the callback
})
}).on('error', (e) => {
console.log(`Got error from Finhockey: ${e.message}`);
callback(e, null);
});
}
module.exports.getFinhockeyData = getFinhockeyData;
Change your router.js like the following...
router.get('/', function(req, res) {
async.series([
function(callback) {
getFinhockeyData(callback)
}
],
function(err, results) {
if(err === null){
console.log('start rendering');
res.render('index', { data: results[0] });
}
})
});

Categories

Resources