Nodejs Problem Send Response Async function & request post - node.js

I have an issue with Async function with post request. I don't success to sendback a string value.
Here is the code
exports.GetQuote = async (req, res, next) => {
try{
var dim = req.body.dimension;
test = new sreq({ ** A LOT OF DATA ** })
const data = JSON.stringify(test);
request.post({
headers: { 'content-type': 'application/json' },
url: 'https://www.xxxx.com/API',
body: data
}, (error, res, body) => {
if (error) {
console.error(error)
console.log('ERROR REQUEST');
return
}
datares = JSON.parse(body);
console.log(datares.value+" "+datares.Code); ***VALUE IS OK***
response = datares.value+" "+datares.Code;
return response; *** NOT RETURN VALUE ***
});
}catch (e){
console.error(e);
}
}
the console.log is correct in nodejs console, but it dont't return the value ?
Have i missed something?
Thanks for Help

You are not catching the return value. You could define a variable outside the callback function.
exports.GetQuote = async (req, res, next) => {
try{
let response;
var dim = req.body.dimension;
test = new sreq({ ** A LOT OF DATA ** })
const data = JSON.stringify(test);
request.post({
headers: { 'content-type': 'application/json' },
url: 'https://www.xxxx.com/API',
body: data
}, (error, res, body) => {
if (error) {
console.error(error)
console.log('ERROR REQUEST');
return
}
datares = JSON.parse(body);
console.log(datares.value+" "+datares.Code); ***VALUE IS OK***
response = datares.value+" "+datares.Code; // sets to the response var we defined above
});
}catch (e){
console.error(e);
}
return response; // Return here
}

With an async function you pause the execution of the code so you don't have the need for a callback. Like with a phone, either you tell the other person to call you back or you await his return to the phone after he done his task you set him.
So for an async function the code can look something like that.
exports.GetQuote = async (req, res, next) => {
try {
// dim variable not used -- > delete
var dim = req.body.dimension;
test = new sreq({ /** A LOT OF DATA **/ })
const data = JSON.stringify(test);
// code stops running until response is here
let response = await request.post({
headers: { 'content-type': 'application/json' },
url: 'https://www.xxxx.com/API',
body: data
})
// error handling I can't say
// since I don't know what is returned here if the call should fail
if(response){
datares = JSON.parse(response.body);
console.log(datares.value + " " + datares.Code); /*** VALUE IS OK ***/
response = datares.value + " " + datares.Code; // sets to the response var we defined above
return response; // Return here
} else{
throw new Error({msg: `something went wrong`})
}
} catch (e) {
console.error(e);
}
}
Do be aware that exports.GetQuote now hold a Promise that is implicitly returned by an async function

Related

How can I send the response.body out of request module function?

I started by creating a return statement in the request function (I have linked a picture) and then console.log it outside of the function but that didn't work out.
My server code
var options = {
'method': 'POST',
'url': 'http://localhost:8080/ES_Part1/api/user/getUser',
'headers': {
'Content-Type': 'application/x-www-form-urlencoded'
},
form: {
'username': username,
'password': password
}
};
requestToApi(options, function(error, response) {
if (error) throw new Error(error);
console.log("Send form data to remote api and to return the user from Spring")
console.log(response.body);
return response.body
});
var fromapi = response.body;
res.end();
Example:
I suggest you use a Promise-based approach here rather than the callback-style that you're using for requestToApi. If you're using the request package, there is a Promise-based version available.
Alternative solution would be to create a promise yourself, like such:
var requestToApiAsPromise = (options) => {
return new Promise((resolve, reject) => {
requestToApi(options, (error, response) => {
if (error) {
reject(error)
return
}
resolve(response.body)
})
})
}
Then you can use this method in your middleware:
app.post("/checkUser", (req, res) => {
async function process() {
try {
var username = req.body.username
var password = req.body.password
var options = {...}
var response = await requestToApiAsPromise(options)
// response => response.body
// do whatever
res.end()
} catch (error) {
next(error)
}
}
process()
})
This method uses async/await so that it lets you write your code as if you were doing things synchronously, so it's making it easier to make asynchronous calls and have them "wait" before the next line gets executed.
👨‍🏫 If you want to get respose.body outside the handler, than you can use this code below 👇:
// an example get function
app.get('/users', async(req, res) => {
var options = {
'method': 'POST',
'url': 'http://localhost:8080/ES_Part1/api/user/getUser',
'headers': {
'Content-Type': 'application/x-www-form-urlencoded'
},
form: {
'username': username,
'password': password
}
};
const result = new Promise((resolve, reject) => {
requestToApi(options, function(error, response) {
if (error) return reject(error);
return resolve(JSON.parse(response.body));
});
})
// make sure, to use async in your function
// because we're using await here
var fromapi = await result;
// It's working here
console.log(fromapi);
res.end();
})
That code above 👆, only an example that you can use to read response.body. If you want to handle the error from that code above, you can use like this code below:
try {
// make sure, to use async in your function
// because we're using await here
var fromapi = await result;
// It's working here
console.log(fromapi);
} catch(ex) {
// print error response
console.log(ex.message);
}
I hope it's can help you 🙏.

Send API GET request with path variables in request-promise-native

I want to do make the same api call as made in this postman photo below :
postman
I have already tried the following code but it only returns some html not the desired response
async function vendor_info() {
const options = {
uri: 'http://**.**.**.**/vendor/:username/pj1527',
json: true,
method: 'GET'
};
let vendor_info = undefined;
await requestPromise(options)
.then((body) => {
// err_token = 0;
vendor_info = body[0];
console.log(body);
// console.log(body[0]);
})
.catch(err => {
vendor_info = undefined;
// err_token = 1;
// console.log(err);
});
return vendor_info;
}
EDIT
It's working now, actually the url should be 'http://.../vendor//pj1527' in the request.
If you are using async await then there is no need to use then and catch blocks, Try like this:
async function vendor_info() {
try {
const options = {
uri: 'http://**.**.**.**/vendor/:username/pj1527',
json: true,
method: 'GET'
};
const vendor_info = await requestPromise(options);
console.log('vendor_info: => ', vendor_info);
return vendor_info;
} catch (err) {
console.log('Error: => ', err);
return err;
}
}
Hope this helps :)

var function calling for unknown reason

I have the code below, and it seems to call the var promiseFeedback is called and I don't know why... This means it is called even when an error occurs when I create document. Whereas is should only be called if there is no err in the createDocument.
Is anyone able to clear up why?
if (json) {
createDocument(documentUrl, context, json, function(res){
var promiseFeedback = callFB (context, res);
var collection = `mydb`
client.createDocument(collection, res, (err, result) => {
if(err) {
context.log(err);
return context.done();
} else {
Promise.all([promiseFeedback]).then(function(results){
context.log("promiseFeedback: " + results[0]);
context.done();
});
}
});
});
}
function callFB(context, res) {
return new Promise((resolve, reject) => {
var requestUrl = url.parse( URL );
var requestBody = {
"id": res.id
};
var body = JSON.stringify( requestBody );
const requestOptions = {
hostname: requestUrl.hostname,
path: requestUrl.path,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body),
}
};
var request = https.request(requestOptions, function(res) {
var data ="";
res.on('data', function (chunk) {
data += chunk
});
res.on('end', function () {
resolve(true);
})
}).on('error', function(error) {
context.log("request error:", error);
resolve(false);
});
request.write(body);
request.end();
});
}
var promiseFeedback = callFB (context, res);
This statement executes callFB immediately, not just assigns another name to the promise. This promise callFB is out of the callback(scope) of err and Promise.all([promiseFeedback]), it runs no matter what the result of client.createDocument is.
To fix this:
Remove var promiseFeedback = callFB (context, res); and change Promise.all([promiseFeedback]) to callFB(context, res). You don't need to use Promise.all as you only have one promise to resolve.
Or you can just move var promiseFeedback = callFB (context, res); into else segment.

Request not returning Write to database. Just shows the console

I am trying to save my response into the database. but it shows only the console and does not return the write to the database.
here is my code....
exports.saveGroups = functions.firestore.document("Users/{user_id}").onWrite((change,context) => {
token_id1 = change.after.data().token_id;
token_email = change.after.data().email;
image = change.after.data().image;
name1 = change.after.data().name;
user_id = context.params.user_id;
console.log('token_id1:' + token_id1);
console.log('token_email:' + token_email);
console.log('Image:' + image);
console.log('name:' + name1);
console.log('user_id' + user_id);
var headers = {
'Authorization': 'key = AAAATmJbwHE:APA91bGIEsq0aioIzAa_7g5JvLX1NPU3z1Gkt6vxB2J-I_9IvllwDJaxjmEp5DPw6ZoEBmXfwiYwICrMuE0kQrvxuHTGPc5YKr3i-JNru-o6AHnrAjq4r7iZWmzUuSmPwu8CbR2kXEIq',
'project_id': '336657629297',
'Content-Type': 'application/json'
}
var options = {
url: 'https://android.googleapis.com/gcm/notification',
method: 'POST',
headers: headers,
json: {'operation': 'create',
'notification_key_name': token_email,
'registration_ids': [token_id1]}
}
const promise = request(options, function (error, response, body) {
tokenName = body.notification_key;
console.log('Key: ' + tokenName); //here it shows me the correct value
return db.collection('Users').doc(user_id).set({name: name1,token_id: token_id1,notification_key: tokenName,image: image,email: token_email}).then(() => { //never reach this line
return console.log("Document successfully written!"); //never returns this console
}).catch(function(error) {
return console.error("Error writing document: ", error);
});
})
return promise; //finishes with status "ok"
});
I've gone through the promises documentation. but I don't find any example to handle the "request" functions.
requires help. thanks in advance.
request supports callback interfaces natively but does not return a promise, which is what you must do within a Cloud Function.
I strongly suggest that you watch these videos from the Firebase team: https://www.youtube.com/watch?v=7IkUgCLr5oA&t=28s and https://www.youtube.com/watch?v=652XeeKNHSk which explain this key concept.
You could use request-promise (https://github.com/request/request-promise) and rp(...) method which "returns a regular Promises/A+ compliant promise" and then do something like:
....
return rp(options) // <- You should return this promise within the Function
.then(function (body) {
const tokenName = body.notification_key;
console.log('Key: ' + tokenName);
return db.collection('Users').doc(user_id).set({name: name1,token_id: token_id1,notification_key: tokenName,image: image,email: token_email});
})
.catch(function (err) {
console.log(err);
});

Q.all array of request promises, not sure how to get the results

Probably an obvious answer to this but I'm not sure what way to take.
request is a node module: https://github.com/request/request
I fill an array of getHistory requests (with different parameters). p = [p1,p2...].
this.app.all('/api/heatmap', function(req,res) {
// fill p here _.each(blabla, p.push(gethistory(someparams...)
var result = [];
function getHistory(params) {
var options = { ...};
var callback = function(error, response, body) {
if(error) { //wtv
} else {
// what to do with the body here ? return body ? result.push(body) ?
}
}
request(options, callback);
}
Q.all(p).then(function() {
});
}
So the problem here is that I when all of the request to be done , put everything in an array/object then send the whole thing to the client. How to have getHistory returning the fetched value (after the request is done ).
Hope it's clear.
The core problem here is that node.js-style callbacks and promises are not compatible. Promises emphasize on return values, node emphasizes on callbacks.
Therefore you need a sort of adapter that wraps node's callback convention properly, a process called Promisifcation. This can be done manually, but it's tedious at best and error-prone when you are not careful. Luckily, since node's conventions are well-established, it can be automated. Q has a few helpers for that, but Bluebird is quite a bit more convenient in this regard.
So the easy way to do it is to switch to Bluebird as the promise library and to use promisifyAll.
var Promise = require('bluebird');
var request = Promise.promisifyAll(require("request"));
this.app.all('/api/heatmap', function(req, res) {
var requests = blabla.map(function (item) {
return request.getAsync({ /* params */ });
});
Promise.all(requests).then(function (responses) {
res.send( JSON.stringify(responses) ); // whatever
}).catch(function (error) {
res.send( "An error ocurred: " + error ); // handle error
});
}
FWIW, here's another answer that shows how the same would look like when done properly with Q:
// promisified request
function requestAsync(options) {
var result = Q.defer();
request(options, function(error, response, body) {
if (error) {
result.reject(error);
} else {
result.resolve(body);
}
});
return result.promise;
}
// returns promises for heatmapVolumes
function getHistory(params) {
return requestAsync({
method: 'GET',
url: 'https://api.kaiko.com/v1/charts/' +
encodeURIComponent(params.exchange) + '/' +
encodeURIComponent(params.pair) +'/history',
qs: params.qs,
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
}).then(function (body) {
return heatmapVolume(body, params.exchange, params.pair);
}).catch(function (error) {
// log detailed error and send less revealing message downstream
console.error('error fetching trades', error);
throw new Error('Something went wrong while fetching trades');
});
}
// usage
this.app.all('/api/heatmap', function(req, res) {
getHistory({
exchange: "foo", pair: "bar", qs: "qux"
}).then(function (heatmap) {
res.send(200, heatmap);
}).catch(function (error) {
res.send(500, error);
});
});
Used Q.deferred and it worked as documented \o/
function getHistory(params) {
var deferred = Q.defer();
var options = {
method: 'GET',
url: 'https://api.kaiko.com/v1/charts/' + params.exchange + '/' + params.pair +'/history',
qs:qs,
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
}
var callback = function(error, response, body) {
if(error) {
console.log('error fetching trades', error);
res.send(500, 'Something went wrong while fetching trades');
} else {
var body = heatmapVolume(body, params.exchange, params.pair);
// console.log("result!", body.exchange, body.pair);
result.push(body);
// return body;
deferred.resolve();
}
}
request(options, callback);
return deferred.promise;
}

Resources