getting and passing object to node js render method - node.js

I am playing around with the alpha vantage API and I am having trouble extracting the data in the else statement for viewing on the front end.
Here is my express route:
app.get('/', (req, res) => {
var url = 'https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=IBM&interval=5min&apikey=apikey';
request.get({
url: url,
json: true,
headers: { 'User-Agent': 'request' }
}, (err, resp, data) => {
if (err) {
console.log('Error:', err);
} else if (resp.statusCode !== 200) {
console.log('Status:', resp.statusCode);
} else {
// data is successfully parsed as a JSON object:
// console.log(data);
}
})
res.render("index");
})
I want to pass the data variable in the else statement to res.render("index" {//get data into here});
I have tried assigning the data variable to another variable outside of request.get({}) but it does not seem to work. Any suggestions? :)

request.get is an asynchronous function, it means that everything inside the callback function below won't be executed immediately, it will wait until the request.get finish calling the endpoint and return the data then callback function will be executed.
// the callback function of `request.get`
(err, resp, data) => {
if (err) {
console.log('Error:', err);
} else if (resp.statusCode !== 200) {
console.log('Status:', resp.statusCode);
} else {
// data is successfully parsed as a JSON object:
// console.log(data);
}
})
In terms of execution flow, your res.render("index"); will be executed first before the callback function of request.get (it waits until the endpoint call finished), that's why you couldn't access data.
So, in order to res.render to access data, it should be put inside callback function so
(err, resp, data) => {
// ...other code
} else {
res.render('index', { data });
}
})
good reference to read about asynchronous https://www.freecodecamp.org/news/nodejs-eventloop-tutorial/

You can render it in the else statement.
app.get('/', (req, res) => {
var url = 'https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=IBM&interval=5min&apikey=apikey';
request.get({
url: url,
json: true,
headers: { 'User-Agent': 'request' }
}, (err, resp, data) => {
if (err) {
console.log('Error:', err);
} else if (resp.statusCode !== 200) {
console.log('Status:', resp.statusCode);
} else {
res.render("index", {data}) // Change to something else
}
})
}}

Related

how to return results to postman using nodejs.i couldn't send result

App.js
app.get('/getCus', function (req, res) {
var id= req.query;
cus_controller.getCus(id,function(response) {
res.json(response);
});
});
cus_controller.js:
module.exports ={
getCus: function (id, callback) {
getCus = function () {
getOneCus(id).then(result => {
callback(result);
}).catch(err => {
callback(err)
})
}
process.nextTick(getCus);
},
}
async function getOneCus(id) {
auth.authClient(function (err, client) {
if (client) {
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
}
}
getCus(id,client, callback);
}
else {
console.error(err);
}
})
}
var getCus = (id, client, callback) => {
client
.invokeApi(null, `/cus/${id}`, 'GET')
.then(function (result) {
console.log(result.data);
return result.data
})
.catch(function (result) {
if (result.response) {
console.dir({
status: result.response.status,
statusText: result.response.statusText,
data: result.response.data
});
} else {
console.log(result.message);
}
});
}
in getCus method getting a response in the console. however, couldn't get a response in postman.it shows 200 status code but didn't get a response. Someone help me, please. I could not find any solution for this.i tried many methods like return data and res.send(data).
You're passing a callback function to getCus, but you never use it. It looks like you meant call it instead of return the result:
client
.invokeApi(null, `/cus/${id}`, 'GET')
.then(function (result) {
console.log(result.data);
callback(result.data); // <-- here
})
Alternatively, if you return the result as you currently do, then you wouldn't pass a callback to getCus but would instead use a .then() structure:
cus_controller.getCus(id).then(function(response) {
res.json(response);
});
But be sure to also return the top-level Promise by returning the call to client.invokeApi:
return client // <-- here
.invokeApi(null, `/cus/${id}`, 'GET')
.then(function (result) {
console.log(result.data);
return result.data
})

How to catch an empty reply from an API {}

I'm currently using Node.js to fetch an API data and converting it into my own Express.js API server to send my own data (The 2 APIs I'm using changes the structure sometime and I have some users that need to keep the same structure).
So here is the code I'm using
app.get('/app/account/:accountid', function (req, res) {
return fetch('https://server1.com/api/account/' + req.params.accountid)
.then(function (res) {
var contentType = res.headers.get("content-type");
if (contentType && contentType.includes("application/json")) {
apiServer = 'server1';
return res.json();
} else {
apiServer = 'server2';
throw "server1 did not reply properly";
}
}).then(server1Reconstruct).then(function (json) {
res.setHeader('Content-Type', 'application/json');
return res.send(json);
}).catch(function (err) {
console.log(err);
}).then(function () {
if (apiServer == 'server2') {
server2.fetchAccount({
accountID: [Number(req.params.accountid)],
language: "eng_us"
})
.then(server2Reconstruct)
.then(function (json) {
res.setHeader('Content-Type', 'application/json');
return res.send(json);
}).catch(function (err) {
console.log(err);
});
}
});
})
To quickly explain the code: I call server1 through a normal Fetch this answer might be {} which is where I have a problem. If the accountid doesn't exist the server returns an JSON response with no errors to grab...
What should I do to be able to catch it... And If I catch it switch to server 2.
(Don't be too confused about server2 call as it's another package).
If I understand your problem correctly, you should follow those steps :
fetch the initial API
call the .json() method on the result - which returns a promise
deal with the json response in the first .then(json => ...), and here check if the result is {} then call server2, else call server1
BTW, your code looks very messy with all those then and catch, I recommend putting some stuff into functions, and using async/await if you can.
Here is some pseudo-code sample that you could use :
function server2Call() {
return server2.fetchAccount({
accountID: [Number(req.params.accountid)],
language: 'eng_us'
})
.then(server2Reconstruct)
.then(function (json) {
res.setHeader('Content-Type', 'application/json');
return res.send(json);
}).catch(function (err) {
console.log(err);
});
}
app.get('/app/account/:accountid', function (req, res) {
return fetch('https://server1.com/api/account/' + req.params.accountid)
.then(res => {
var contentType = res.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return res.json();
} else {
server2Call()
}
})
.then(json => {
res.setHeader('Content-Type', 'application/json');
if (json is {}) return server2Call()
else return res.send(json);
}).catch(function (err) {
console.log(err);
})
});

How to Ensure Data is Returned Before Variable Assignment (Node.js)

I am trying to retrieve data at multiple API endpoints simultaneously and aggregate the result to be sent back to the client as one response.
However, when trying to return data from async.parallel, I get an undefined result. I think this is because the result code is being executed before it is returned, but I am not sure.
I would like to pass an array of ids to the server, get their metadata, combine their metadata into one object, and send a single object back to the client.
function doSomething() {
for(let i=0; i<req.map.length; i++) {
stack[i] = (callback) => {
let data = subroute(req, res, req.map[i])
callback(null, data)
}
}
async.parallel(stack, (err, result) => {
if(err) {
// Handle error
} else {
console.log(result) // Logs undefined
res.json([result]) // Would like to send this result back to the client
}
})
}
function subroute(req, res, num) {
request({
url: 'https://example.com/api/endpoint/' + num
},
(error, response, body) => {
if(error) {
res.json({ "error": error })
} else {
let i = {
name: body.name,
age: body.age,
}
return i
}
})
}
How can I accumulate the results of many API endpoint responses on the server and send the back as one response to the client?
Change
let data = subroute(req, res, req.map[i])
callback(null, data)
to
subroute(req, res, req.map[i], callback)
Subsequently, change subroute to receive the callback and return the return
function subroute(req, res, num, cb) {
request({
url: 'https://example.com/api/endpoint/' + num
},
(error, response, body) => {
if(error) {
res.json({ "error": error })
cb(<whatever error you would want to pass>);
} else {
let i = {
name: body.name,
age: body.age,
}
cb(null, i);
}
})
}
By doing let data = subroute(req, res, req.map[i]) you are assigning the result of subroute call to data. Notice subroute does not have a return statement in the function body so data will always be undefined.

Is this a best practice to make API call in Express 4.0

The application is making API request to the server and returning back the response received. Here would like to understand
(i) Is it right way to make API call and receive response from the server.
(ii) if return statement passing back the value is right way
The module is called as
str = apiRequest(search, lang);
And the module is
var https = require('https');
function apiRequest(search, lang) {
var options = {
host: 'localhost:8080',
path: '/api?search=' + search + '&lang=' + lang
};
function resData(res) {
console.log("Status Code : ", res.statusCode);
var str = '';
res.on('data', function (chunk) {
str += chunk;
});
res.on('end', function () {
return JSON.parse(str); // Is this a right way to return data
});
res.on('error', function(e) {
console.error(e);
});
}
https.get(options, resData).end();
};
No, this will not work. I will comment the relevant parts below.
Instead of having this in apiRequest:
function apiRequest(search, lang) {
function resData(res) {
// ...
res.on('end', function () {
return JSON.parse(str); // Is this a right way to return data
});
// ...
}
https.get(options, resData).end();
}
and calling it with:
str = apiRequest(search, lang);
You should either pass a callback or return a promise.
Using callbacks
Your apiRequest function can take an additional argument, a callback:
function apiRequest(search, lang, callback) {
function resData(res) {
// ...
res.on('end', function () {
callback(null, JSON.parse(str));
});
res.on('error', function(e) {
console.error(e);
callback(e);
});
// ...
}
https.get(options, resData).end();
}
Now you can use it as:
apiRequest(search, lang, function (error, str) {
if (err) {
// you have error
} else {
// you have your str here
}
});
Using promises
Your apiRequest function can return a promise:
function apiRequest(search, lang, callback) {
return new Promise(function (resolve, reject) {
function resData(res) {
// ...
res.on('end', function () {
resolve(JSON.parse(str));
});
res.on('error', function(e) {
console.error(e);
reject(e);
});
// ...
}
https.get(options, resData).end();
}
}
Now you can use it as:
apiRequest(search, lang)
.then(function (str) {
// you have your str here
})
.catch(function (err) {
// you have error
});
This is not tested so there might be some minor mistakes but that is the general idea. When I find some errors I'll update the answer.
Summary
To sum it up, there are two styles that you can use to compose asynchronous functions like that: callbacks or promises. You will not be able to just return the data because return is fundamentally synchronous - you have to have something to return right away - unless what you return is a promise that can get resolved or rejected later.
Parsing JSON
What should also keep in mind that you should always run JSON.parse() inside a try {} catch {} block to handle errors of incorrect JSON or otherwise the entire app could crash. JSON.parse() throws exceptions on bad input. See this answer for more info.
This will additionally complicate your code but you can avoid that complication and make it even simpler by using the request module - see updates below.
Simpler examples
Callbacks
To have a working example that is simpler and your don't have to manually parse JSON, consider this code that I just wrote, based on your example but calling a GitHub API so it can be tested by everyone - it prints someone's website given his GitHub nickname but otherwise works similarly to your code:
'use strict';
var request = require('request');
function apiRequest(search, callback) {
var options = {
url: 'https://api.github.com/users/' + search,
json: true,
headers: {'User-Agent': 'request'}
};
function resData(err, res, data) {
if (err) {
callback(err);
} else if (res.statusCode !== 200) {
callback(res.statusCode);
} else {
// data is already parsed as JSON:
callback(null, data.blog);
}
}
request.get(options, resData);
}
apiRequest('rsp', function (err, data) {
if (err) {
console.log('Error:', err);
} else {
console.log('Data:', data);
}
});
This is an example using callbacks.
Promises
And here is an example using promises:
'use strict';
var request = require('request');
function apiRequest(search, callback) {
return new Promise(function (resolve, reject) {
var options = {
url: 'https://api.github.com/users/' + search,
json: true,
headers: {'User-Agent': 'request'}
};
function resData(err, res, data) {
if (err) {
reject(err);
} else if (res.statusCode !== 200) {
reject(res.statusCode);
} else {
// data is already parsed as JSON:
resolve(data.blog);
}
}
request.get(options, resData);
});
}
apiRequest('rsp')
.then(function (data) {
console.log('Data:', data);
})
.catch(function (err) {
console.log('Error:', err);
});
Simplified
It can be simplified even further by using fat arrow functions and anonymous functions and object literals:
'use strict';
var request = require('request');
function apiRequest(search, callback) {
return new Promise((resolve, reject) => {
request.get({
url: 'https://api.github.com/users/' + search,
json: true,
headers: {'User-Agent': 'request'}
}, (err, res, data) => {
if (err) {
reject(err);
} else if (res.statusCode !== 200) {
reject(res.statusCode);
} else {
resolve(data.blog);
}
});
});
}
apiRequest('rsp')
.then(data => console.log('Data:', data))
.catch(err => console.log('Error:', err));
More info
You can see some other answers where I explain the difference between callbacks and promises and how to use the together in more detail, which you may find helpful:
A detailed explanation on how to use callbacks and promises
Explanation on how to use promises in complex request handlers
An explanation of what a promise really is, on the example of AJAX requests
An explanation on how to handle errors in callbacks and promises

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