Response doesn't wait for async function - node.js

I found the aproach how to put the async function into a router endpoint and tried to implement it but not suceed. E.g. this link zellwk.com
The server sends only empty string instead of a huge string that is on snippet.host.
async function downloadData(url) {
const options = {
'method': 'GET', 'url': url, 'headers': {}, formData: {}
};
let result = '';
await request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body)
result = response.body
});
return {error: 0, text: result}
}
app.get('/token/:token', jsonParser, async function (req, res) {
console.log(req.params.token) //Don't mind this token
try {
let message = await downloadData('https://snippet.host/xgsh/raw')
res.send(message)
} catch (e) {
console.log(e);
res.sendStatus(500);
}
});
How to make it wait for the download?
EDIT
I implemented node-fetch and got this:
{"error":0,"text":{"size":0}}
What did I wrong?
async function downloadData(url) {
const result = await fetch(url)
return {error: 0, text: result}
}
app.get('/token/:token', jsonParser, async function (req, res) {
console.log(req.params.token)
try {
let message = await downloadData('https://snippet.host/xgsh/raw')
res.send(message)
} catch (e) {
console.log(e);
res.sendStatus(500);
}
});

in case of fetch call the response body need to be parsed first which you are missing.
async function downloadData(url) {
const res = await fetch(url)
if (res && res.status != 200)
throw new Error("status code other than 200 received")
let json = await res.json()
return {error: 0, text: json}
}
app.get('/token/:token', async function (req, res) {
console.log(req.params.token)
try {
let message = await downloadData('https://snippet.host/xgsh/raw')
// let json = await message.json()
res.send(message)
} catch (e) {
console.log(e);
res.sendStatus(500);
}
});
i tried calling the api but every time it is returning 404 . so json cant be called in that case. but if 200 is returned then you call json and return that accordingly.

You can only usefully await a promise.
request doesn't return a promise, it takes a callback function instead.
This is one of the reasons that request was deprecated two years ago.
Replace request with something which supports promises such as node-fetch, the native fetch that was added to Node.js recently, or axios.

In case you must use request (eventhough it's deprecated); or in case you find a similar situation with a old function that doesn't return a promise; you can turn your request into a function that returns a promise.
Either write your own wrapper, in the form of
function requestPromise(options) {
return new Promise(
(resolve,reject) => request(options,
(err,response) => if (err) reject(err) else resolve(response)));
}
or, given that request's callback is in the form of function(err,response), directly use util.promisify
async function xxxx(...) {
try {
...
let response = await util.promisify(request)(options);
...
}

Because request isn't actually an async function that returns a Promise, you can create your own async version of request.
function asyncRequest(options) {
return new Promise((resolve, reject) => {
request(options, (err, response) => {
if (err) {
reject(err)
} else {
resolve(response)
}
})
})
}
Then in your project rewrite your downloadData function to be:
async function downloadData(url) {
const options = {
method: "GET",
url: url,
headers: {},
formData: {},
}
const result = await asyncRequest(options)
return { error: 0, text: result.body }
}

Related

how to wrap axios function and let parent to wait?

here's I've my axios function wrapped within a custom function:
async function getOrder(orderId) {
// ...
await axios({
method: 'post',
url: endpoint + "/mymethod",
data: request
}).then(function (response) {
var data = response.data.result;
return data;
}).catch(function (error) {
return { "error": error };
});
}
but if than i call that function:
router.get('/getOrder/:id', (req, res) => {
let result = getOrder(req.params.id);
res.json(result);
})
it returns nothing (since its async and don't wait the .then()).
what's the best way to wrap axios/async function and call from outside?
I think you're missing await in your codes:
router.get('/getOrder/:id', async (req, res) => {
let result = await getOrder(req.params.id);
res.json(result);
})
====================
[New Update]
for me in API function:
async getOrder(orderId) {
try {
const response = await axios.post(endpoint + "/mymethod")
return response.data
} catch (error) {
return { "error": error }
}
}
and get the result:
router.get('/getOrder/:id', async (req, res) => {
let result = await getOrder(req.params.id);
res.json(result);
})
===========
[New Update2] here is my sample async/await function with axios
const axios = require("axios")
async function getOrder(orderId) {
try {
const response = await axios.get("http://google.com")
return response.data
} catch (error) {
return { "error": error }
}
}
async function main() {
let result = await getOrder();
console.log(result, "##")
}
main()
====================
[New Update3] new Promise with axios:
const axios = require("axios")
async function getOrder(orderId) {
// try {
// const response = await axios.get("http://google.com")
// return response.data
// } catch (error) {
// return { "error": error }
// }
return await new Promise((resolve, reject) => {
axios({
method: 'get',
url: "http://google.com"
}).then(function (response) {
var data = response.data
resolve(data)
}).catch(function (error) {
reject({ "error": error })
});
})
}
async function main() {
let result = await getOrder();
console.log(result, "##")
}
main()
I think you are missing an await so you do not return a promise but wait for the result of getOrder to be passed in res.json :
router.get('/getOrder/:id', async (req, res) => {
let result = await getOrder(req.params.id);
res.json(result);
})

node js call a promise function inside a chain .then

Hello I am new working with asynchronous calls I hope you can help me.
I have a .then chain where I want to call in the middle of the chain a function that returns a promise but it happens to me that the chain does not wait for it to have the result of the promise function and continues without waiting
var User = require('../models/user_model');
var mapbox = require('./helper/request_api_mapbox');
findOne_mapbox: (req, res) => {
User.findById(req.params.id)
.then(user => {
...
return user.address;
})
.then(function (adress_to_mapbox)
{
// mapbox.connect_mapbox returns a promise
mapbox.connect_mapbox(adress_to_mapbox)
.then(mapbox_coordinates => {
//inside this console.log is not read it
console.log("2 then mapbox_coordinates ", mapbox_coordinates)
return Promise.resolve(body);
})
})
.then( mapbox_coordinates => {
// in this console.log mapbox_coordinates returns undefined
console.log("last promise mapbox_coordinates",
mapbox_coordinates)
// I want to return mapbox_coordinates
return res.status(200).send({
"response": "I only receive this string "
});
})
}
the promise function is:
'use strict'
const rp = require('request-promise');
const access_token_mapbox = 'bla bla bla private';
function connect_mapbox(adress_to_mapbox) {
return new Promise((resolve, reject) => {
var options = {
method: 'GET',
uri: 'https://api.mapbox.com/geocoding/v5/mapbox.places/' + adress_to_mapbox + '.json?access_token=' + access_token_mapbox,
json: true // Automatically stringifies the body to JSON
};
rp(options)
.then(body => {
if (body.hasOwnProperty('errcode') && body.errcode != 0) {
return Promise.reject(body);
}
console.log("inside connect_mapbox function on mapbox_model 2", body)
return Promise.resolve(body);
})
.catch(err => {
debug(err);
return Promise.reject(err);
})
})
}
module.exports = { connect_mapbox };
Inside the promise function I can see in my console.log that it makes the api call properly and the response body is ok.
I am starting in the programming, I solved the problem in the following way although I don't know if it is the most correct
I solved it by converting the connect_mapbox promise function to async await and the .then chain to async await like so:
'use strict'
const rp = require('request-promise');
const access_token_mapbox = 'bla bla bla private';
async function connect_mapbox(adress_to_mapbox) {
try {
var options = {
method: 'GET',
uri: 'https://api.mapbox.com/geocoding/v5/mapbox.places/' + adress_to_mapbox + '.json?access_token=' + access_token_mapbox,
json: true // Automatically stringifies the body to JSON
};
let response = await rp(options);
console.log("inside connect_mapbox function on mapbox_model", response)
if (response.hasOwnProperty('errcode') && body.errcode != 0) {
return Promise.reject(response);
}
return Promise.resolve(response);
}catch(error) {
return Promise.reject(error);
}
}
module.exports = { connect_mapbox };
And the .then chain like
var User = require('../models/user_model');
var mapbox = require('./helper/request_api_mapbox');
findOne_mapbox: (req, res) => {
User.findById(req.params.id)
.then(user => {
...
return user.address;
})
.then(async function (adress_to_mapbox)
{
console.log("adress_to_mapbox thit could be deleted", adress_to_mapbox)
let mapbox_response = await mapbox.connect_mapbox(adress_to_mapbox)
console.log("async mapbox_response", mapbox_response)
return res.status(200).send({
mapbox_response
});
})
...
}

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 :)

How to call an API inside a cloud function?

I'm working on an Actions on Google chatbot using Dialogflow and Cloud Functions. The runtime is Node.js 6.
Why does this function return the empty string?
function getJSON(url) {
var json = "";
var request = https.get(url, function(response) {
var body = "";
json = 'response';
response.on("data", function(chunk) {
body += chunk;
json = 'chunk';
});
response.on("end", function() {
if (response.statusCode == 200) {
try {
json = 'JSON.parse(body)';
return json;
} catch (error) {
json = 'Error1';
return json;
}
} else {
json = 'Error2';
return json;
}
});
});
return json;
}
This is the intent in which I want to access the json data:
app.intent('test', (conv) => {
conv.user.storage.test = 'no change';
const rp = require("request-promise-native");
var options = {
uri: 'https://teamtreehouse.com/joshtimonen.json',
headers: {
'User-Agent': 'Request-Promise'
},
json: true // Automatically parses the JSON string in the response
};
rp(options)
.then(function (user) {
conv.user.storage.test = user.name;
})
.catch(function (err) {
conv.user.storage.test = 'fail';
});
conv.ask(conv.user.storage.test);
});
You can try to use the request module for node.js, I tried my self reproducing your use case and worked fine. The code should be something similar to this:
const request = require('request');
request(url, {json: true}, (err, res, body) => {
if (err) { res.send(JSON.stringify({ 'fulfillmentText': "Some error"})); }
console.log(body);
});
Also, you need to add "request": "2.81.0" in the dependencies section inside your package.json file.
The function returns the empty string because https sets up a callback function, but the program flow continues to the return statement before the callback is called.
In general, when working with Dialogflow Intent Handlers, you should be returning a Promise instead of using callbacks or events. Look into using request-promise-native instead.
Two clarifying points:
You must return the Promise. Otherwise Dialogflow will assume the Handler has completed. If you return the Promise, it will wait for the Promise to finish.
Everything you want to send back must be done inside the then() block. This includes setting any response. The then() block runs after the asynchronous operation (the web API call) completes. So this will have the results of the call, and you can return these results in your call to conv.ask().
So it might look something like this:
return rp(options)
.then(function (user) {
conv.add('your name is '+user.name);
})
.catch(function (err) {
conv.add('something went wrong '+err);
});
You can use Axios as well
To install Axios : npm install axios
OR
You can add it in package.json as a dependency
"dependencies": {
"axios": "^0.27.2",
}
index.js
const axios = require('axios').default;
exports.makeRequest = async (req, res) => {
axios.get('https://jsonplaceholder.typicode.com/todos/1')// Dummy URL
.then(function (response) {
// handle success
res.status(200).json({
success:true,
result:response.data
})
})
.catch(function (error) {
// handle error
console.log(error);
})
};
OR
const Axios = require("axios");
exports.makeRequest = async (req, res) => {
const { data } = await
Axios.get('https://jsonplaceholder.typicode.com/todos/1')
res.status(200).send({data})
};

Resources