I am setting up an API connection via Node.js. I had some predefined cURL code which I converted into Node.js code, which I have provided below. Until now everything works fine, I am displaying the value I need (token) inside the console window.
However, I am wondering how I can use this token variable in another function? So I somehow have to save it as a global variable, but until now that didn't work.
var request = require('request');
var headers = {
'content-type': 'application/x-www-form-urlencoded',
'Authorization': 'XXXXXXXXXXXXXXXXX'
};
var dataString = 'grant_type=client_credentials';
var options = {
url: 'XXXXXXXXXXXXXXXX',
method: 'POST',
headers: headers,
body: dataString
};
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
var str = body;
token = str.split('\"')[3];
console.log(token);
}
}
request(options, callback);
You can access token only when request() complete and calls the callback function. This is due to the non-blocking nature of node.js - when you start a request the code doesn't block and you can access its response only when it completes and call the callback function. Hence you first define the callback function and pass it to request as an argument. If you want to access token you can create another function and call it inside the callback.
var request = require('request');
var headers = ...
var dataString = ...
var options = ...
function doStuffWithToken(token) {
console.log(token)
}
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
var str = body;
token = str.split('\"')[3];
doStuffWithToken(token);
}
}
request(options, callback);
You can also use promises for better code:
var request = require('request');
function getToken() {
var headers = ...
var dataString = ...
var options = ...
return new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if (error) return reject(error)
if (response.statusCode == 200) {
var str = body;
token = str.split('\"')[3];
resolve(token);
}
}
}
}
getToken()
.then((token) => {
// here you can access the token
console.log(token)
})
.catch((error) => {
console.error('unable to retrieve token', error)
})
Here we create a wrapper around our request. getToken() returns a promise object that you can use to register two handlers for when it resolves successfully and for when it rejects and throw an error.
You can use getToken() also with the await/async keyword
var request = require('request');
function getToken() {
var headers = ...
var dataString = ...
var options = ...
return new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if (error) return reject(error)
if (response.statusCode == 200) {
var str = body;
token = str.split('\"')[3];
resolve(token);
}
}
}
}
async function main() {
let token = await getToken()
console.log(token)
}
main()
.then(...)
.catch(...)
Further readings:
Don't block the event loop
Related
I created a login function that receives the mail and the pass, to receive the jwt. I have tried that the function returns the jwt but I have not succeeded.
This is the method that I have developed, it has a post request that sends the mail and pass parameters. in the resp variable I try to save the request response, but when invoking the function it prints :
undefined.
login(mail, pass) {
var options = {
'method': 'POST',
'url': 'https://inventario.demos.adlnetworks.com/api/login',
'headers': {
'Content-Type': 'application/json'
},
body: JSON.stringify({ "email": mail, "password": pass })
};
var resp;
var req = request(options, function(error, response) {
if (error) throw new Error(error);
resp = response.body;
});
return resp;
}
The problem is that "request" is an async function. You can't do this
var resp;
var req = request(options, function(error, response) {
if (error) throw new Error(error);
resp = response.body;
});
return resp;
Because "resp" always be undefined. You would need to do something like this
var resp;
var req = request(options, function(error, response) {
if (error) throw new Error(error);
return response.body;
});
But it wont work for you.
The short and easy solution is change the library to make http request, and use "async" and "await" to use easily async functions.
For example:
const fetch = require('node-fetch');
async function main(){
const data = await login();
console.log(data);
}
async function login(){
const url = "https://jsonplaceholder.typicode.com/posts";
const data = {
title: 'foo22222',
body: 'ba222r',
userId: 1
};
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(data),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
});
const json = await response.json()
return json;
}
main();
In this case i use "node-fetch" library and consume a backend (in login function) that create a post and return its response.
I'm new to node js and I would like to write information within a callback function to my firebase database.
I've been searching and it seems that the callback is asynchronous. How can I use firestore in this callback?
exports.registerRestaurantPayout = functions.firestore.document('*********')
.onCreate(async (paymentSnap, context) => {
var request = require('request');
var authCode = paymentSnap.data().auth_code;
var firstString = 'client_secret=********&code=';
var secondString = '&grant_type=authorization_code';
var dataString = firstString + authCode + secondString;
var options = {
url: 'https://connect.stripe.com/oauth/token',
method: 'POST',
body: dataString
};
function callback(error, response, body) {
if (!error && response.statusCode === 200) {
console.log(body);
return await firestore.document('***********')
.set({'account': body}, {merge: true});
//return await paymentSnap.ref.set({'account': body}, {merge: true});
}else{
//return await paymentSnap.ref.set({'error' : error}, { merge: true });
}
}
request(options, callback);
});
I get the following error Parsing error: Unexpected token firestore even though I can use firestore outside of the callback. The specific problem is the return statement in the callback
In a Cloud Function you should use promises to handle asynchronous tasks (like the HTTP call to the stripe API, or the write to the Realtime Database). By default request does not return promises, so you need to use an interface wrapper for request, like request-promise, and adapt your code along the following lines:
const rp = require('request-promise');
exports.registerRestaurantPayout = functions.firestore.document('*********')
.onCreate((paymentSnap, context) => {
var authCode = paymentSnap.data().auth_code;
var firstString = 'client_secret=**********=';
var secondString = '&grant_type=authorization_code';
var dataString = firstString + authCode + secondString;
var options = {
method: 'POST',
uri: 'https://connect.stripe.com/oauth/token',
body: dataString,
json: true // Automatically stringifies the body to JSON
};
return rp(options)
.then(parsedBody => {
return paymentSnap.ref.set({'account': parsedBody}, {merge: true});
})
.catch(err => {
return paymentSnap.ref.set({'error' : err}, { merge: true });
});
});
I would also suggest that you watch the two following "must see" videos from the Firebase team, about Cloud Functions and promises: https://www.youtube.com/watch?v=7IkUgCLr5oA and https://www.youtube.com/watch?v=652XeeKNHSk.
I have using a code snippet which will return a value after post rest call to an api.
But where ever i am calling the function its not returning the value and prints undefined.
when ever i will call any where getAccessToken(), its says undefiuned, but ifi print the value am getting the output.
How do the called will get the return value, do i need to change anything in the below code.
Thanks
var getAccessToken = exports.getAccessToken = function (res) {
// body...
const request = require('request');
const authKey='EAcEa4o4SkBLo9IpZpW4Y7oDn7d6b30GlouNh28pJ6Q='
const ContentType='application/x-www-form-urlencoded' ;
var postData={
'grant_type':'client_credentials'
};
const options = {
url: 'https://xyz/v1/login',
method: 'POST',
headers: {
'Content-Type': ContentType,
'Authorization':authKey
},
body:require('querystring').stringify(postData)
};
var token;
request(options, function(errror, response, body) {
//console.log(JSON.parse(body));
token= JSON.parse(body).access_token;
});
return token;
}
Your function doesn't return anything. You may use async/await, promises or callbacks to fix it.
exports.getAccessToken = async (res) => {
...
return await request(...)
}
OR
exports.getAccessToken = function(res) {
...
return new Promise(function(resolve, reject) {
...
request(options, function(errror, response, body) {
var token = JSON.parse(body).access_token;
resolve(token);
}
});
}
// Use it like
getAccessToken().then(function(token) { ... });
OR
exports.getAccessToken = function(res, cb) {
...
request(options, function(errror, response, body) {
var token = JSON.parse(body).access_token;
cb(token);
}
}
// Use it like
getAccessToken(res, function(token) { ... });
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I have a function that returns a value which it gets from an http GET request in nodejs. How do I wait for the async request function to finish and return the result from the function that generates the request.
I have checked async library in npm, but that doesn't solve my problem.
Thanks for the help!!
function daemonGetNodeStatus(kubeURL, nodeName) {
console.log(nodeName);
var request = require("request");
var options = {
method: 'GET',
url: kubeURL+'/api/v1/nodes/'+nodeName+'/status',
headers: {
'Cache-Control': 'no-cache',
'Authorization': 'Bearer '+constants.accessToken
}
};
request(options, function(error, response, body) {
if (error)
throw new Error(error);
var bodyJSON = JSON.parse(body);
var result = [];
var temp = {};
for (var i = 0; i < bodyJSON.status.conditions.length; i++) {
if(bodyJSON.status.conditions[i].status == "True"){
result.push(bodyJSON.status.conditions[i].type);
}
}
console.log(result);
});
};
You could use a Promise.
function daemonGetNodeStatus(kubeURL, nodeName) {
console.log(nodeName);
var request = require("request");
var options = {
method: 'GET',
url: kubeURL+'/api/v1/nodes/'+nodeName+'/status',
headers: {
'Cache-Control': 'no-cache',
'Authorization': 'Bearer '+constants.accessToken
}
};
return new Promise((resolve, reject) => {
request(options, function(error, response, body) {
if (error)
reject(new Error(error));
var bodyJSON = JSON.parse(body);
var result = [];
var temp = {};
for (var i = 0; i < bodyJSON.status.conditions.length; i++) {
if(bodyJSON.status.conditions[i].status == "True") {
result.push(bodyJSON.status.conditions[i].type);
}
}
resolve(result);
});
}
daemonGetNodeStatus(url, name).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
function daemonGetNodeStatus(kubeURL, nodeName, callback) {
console.log(nodeName);
var request = require("request");
var options = {
method: 'GET',
url: kubeURL+'/api/v1/nodes/'+nodeName+'/status',
headers: {
'Cache-Control': 'no-cache',
'Authorization': 'Bearer '+constants.accessToken
}
};
request(options, function(error, response, body) {
if (error)
{
callback(error);
} else {
var bodyJSON = JSON.parse(body);
var result = [];
var temp = {};
for (var i = 0; i < bodyJSON.status.conditions.length; i++) {
if(bodyJSON.status.conditions[i].status == "True"){
result.push(bodyJSON.status.conditions[i].type);
}
}
callback(null, result);
}
});
}
One of the best things about Node is it's asynchronous. It might be hard to understand in the beginning, but once you know, it's the best thing. It's also the reason Node is so fast.
So, when you have to run an asynchronous function, you also send an extra function to the asynchronous function called a callback function which will be executed once the async operation is done. The above code illustrates this.
Then you can use the function like this:
daemonGetNodeStatus('http://kube.com/something', 'name', function(err, result){
if(err) {console.log(err); }
else {
console.log(result);
// do whatever you want with the async result
}
});
I'd like to answer with one more approach - async\await. If you have NodeJS v8 installed, better to stick with async\await.
Also, I made some improvements, feel free to ignore them if you don't like it.
1) async/await offers to you more convenient way to write and deal with asynchronous code. Instead of callbacks or Promise chains, you just write await.
2) Instead of iterating through array via for-loop you can use filter and map to find all the k8s conditions where status is True.
3) json field in request options will parse response as JSON and returns to you already parsed JSON value.
const request = require("request-promise");
async function daemonGetNodeStatus(kubeURL, nodeName) {
const options = {
json: true,
method: 'GET',
url: kubeURL + '/api/v1/nodes/' + nodeName + '/status',
headers: {
'Cache-Control': 'no-cache',
'Authorization': 'Bearer '+constants.accessToken
}
};
const response = await request(options);
return response.status.conditions.filter(cond => cond.status === 'True').map(cond => cond.type);
});
UPD
Any async function returns Promise, so you need to await it or call it with then\catch:
async function someAnotherAsyncFunction() {
const types = await daemonGetNodeStatus('KUBE_URL', 'NODE_NAME');
// do something with types
}
or
daemonGetNodeStatus.then(types => doSomethingWithTypes(types)).catch(error => doSomethingWithError(error));
I have this koa route /landing which is resulting in 404.
function* landing() {
//this.body = "response"; //1
var request = require('request');
request.post('http://url.com/resource',
{ json: { key: "post data"} },
function (error, response, body) {
if (!error && response.statusCode == 200) {
var token = body.data;
getListByToken(token, function(list){
this.body = list; //2
});
}
});
}
See comment in the top //1 - that is how you define body of a response in koa in a route. Instead of //1 I want to send response from //2 i.e. from within that request.get.
When a user is routed to /landing a post request has to fetch some data from a url. That fetched data is to be used by getListByToken to bring some other data, list, which should be sent to the user. The above code should have worked but it results in 404 Not Found response by koa.
We can use promises, simple and clean way of managing asynchronous codes.
var request = require('request-promise');
. . .
function* landing() {
try {
let response = yield request( {
method: 'POST',,
url: 'http://url.com/resource',
headers: { 'content-type': 'application/json' },
body: JSON.stringify( { json: { key: "post data"} } )
} );
this.body = yield new Promise( function( resolve, reject ) {
if ( response.statusCode == 200 ) {
getListByToken( response.body.token, function( list ) {
resolve( list );
} );
}
} );
} catch ( err ) {
/* do something with your errors */
}
}
Solved it with q. It makes koa hold response until yield happens.
var q = require('q');
function* landing() {
var deferred = q.defer();
var request = require('request');
request.post('http://url.com/resource',
{ json: { key: "post data"} },
function (error, response, body) {
if (!error && response.statusCode == 200) {
var token = body.data;
getListByToken(token, function(list){
deferred.resolve(repolist);
});
}
});
this.body = yield deferred.promise;
}
Thanks to https://stackoverflow.com/a/22159513/1128379