Creating a simple Node.Js
app where need to display data from two APIs, where both APIs returns multiple objects with an ID.
Need to display data from both these APIs on a single page, and somehow need to fetch the data from the two API based on the ID.
API 1 response looks like this:
{
"hikes": [
{
"id": 1,
"active": true,
"name": "Mt. Everest",
},
{
"id": 2,
"active": true,
"name": "K2",
},
{
"id": 3,
"active": true,
"name": "Mt. Kinley",
},
]
}
API 2 response looks like this:
{
"hikes": [
{
"id": 1,
"slots": 50,
"available": 23,
},
{
"id": 2,
"slots": 20,
"available": 1,
},
{
"id": 3,
"slots": 43,
"available": 20,
},
]
}
Need to pull both APIs, fetch the data and render on a page to display "name", "slots", and "available".
This far managed to pull one of the APIs, and pass the data to a rendered index.ejs
page, but I am not sure how I should pull the second API and some how fetch the data`s.
My code at the moment looks like this:
var port = process.env.PORT || 3000,
express = require("express"),
request = require("request"),
app = express();
app.set("view engine", "ejs");
var hikes = {
url: "",
headers: {
'Identifier': identifier
}
};
var availability = {
url: "",
headers: {
'Identifier': identifier
}
};
app.get("/", function(req, res){
function callback(error, response, body){
if(!error && response.statusCode == 200){
var data = JSON.parse(body);
res.render("index", {data: data});
})
}
}
request(hikes, callback);
});
app.listen(port, function(){
console.log("Running");
});
In my index.ejs, I for now have created a simple loop to print the names:
<% data["hikes"].forEach(function(hike){ %>
<p><%= hike.name %></p>
<% }) %>
Any ideas on how to solve this?
Thanks!
Creating a simple Node.Js
app where need to display data from two APIs, where both APIs returns multiple objects with an ID.
Need to display data from both these APIs on a single page, and somehow need to fetch the data from the two API based on the ID.
API 1 response looks like this:
{
"hikes": [
{
"id": 1,
"active": true,
"name": "Mt. Everest",
},
{
"id": 2,
"active": true,
"name": "K2",
},
{
"id": 3,
"active": true,
"name": "Mt. Kinley",
},
]
}
API 2 response looks like this:
{
"hikes": [
{
"id": 1,
"slots": 50,
"available": 23,
},
{
"id": 2,
"slots": 20,
"available": 1,
},
{
"id": 3,
"slots": 43,
"available": 20,
},
]
}
Need to pull both APIs, fetch the data and render on a page to display "name", "slots", and "available".
This far managed to pull one of the APIs, and pass the data to a rendered index.ejs
page, but I am not sure how I should pull the second API and some how fetch the data`s.
My code at the moment looks like this:
var port = process.env.PORT || 3000,
express = require("express"),
request = require("request"),
app = express();
app.set("view engine", "ejs");
var hikes = {
url: "https://api./hikes",
headers: {
'Identifier': identifier
}
};
var availability = {
url: "https://api./hikes",
headers: {
'Identifier': identifier
}
};
app.get("/", function(req, res){
function callback(error, response, body){
if(!error && response.statusCode == 200){
var data = JSON.parse(body);
res.render("index", {data: data});
})
}
}
request(hikes, callback);
});
app.listen(port, function(){
console.log("Running");
});
In my index.ejs, I for now have created a simple loop to print the names:
<% data["hikes"].forEach(function(hike){ %>
<p><%= hike.name %></p>
<% }) %>
Any ideas on how to solve this?
Thanks!
Share Improve this question edited Sep 24, 2018 at 18:21 objectclass asked Sep 18, 2018 at 12:51 objectclassobjectclass 2241 gold badge6 silver badges20 bronze badges4 Answers
Reset to default 1If I understood correctly, I assume you are trying to fetch data from two APIs and want to merge the data into single array of objects based on object id and pass it to view. if this is the case then you can use https://www.npmjs./package/async to fetch data parallel from both the APIs then merge the data in to one array of objects and pass it to your view. Following code will help you understand the implementation.
var port = process.env.PORT || 3000,
express = require("express"),
request = require("request"),
app = express();
var async = require('async');
app.set("view engine", "ejs");
var hikes = {
url: "https://api./hikes",
headers: {
'Identifier': identifier
}
};
var availability = {
url: "https://api./hikes",
headers: {
'Identifier': identifier
}
};
app.get("/", function(req, res) {
function callback(error, response, body, cb) {
if(error || response.statusCode != 200)
return cb(true);
cb(null, JSON.parse(body).hikes);//instead of sending data directly to view, send it to async callback to merge it latter
}
var tasks = { // tasks to run in parallel
hikes: function (cb) {
request(hikes, function (error, response, body) {
callback(error, response, body, cb);
});
},
availability: function (cb) {
request(availability, function (error, response, body) {
callback(error, response, body, cb);
});
}
};
async.parallel(tasks, function (err, resp) {
if(err) {
//handle error here, the error could be caused by any of the tasks.
return;
}
var availabilityIdMap = resp.availability.map(function (availability) { return availability.id; });//get an array of all the availability ids
var data = resp.hikes.map(function (hike) { //merging hike to corresponding availability object
var availabilityIndex = availabilityIdMap.indexOf(hike.id); // finding the availability against the hike id.
if(availabilityIndex < 0) //availability not found, just return hike
return hike;
var matchingAvailabilityObj = resp.availability[availabilityIndex]; //get the matching availability object
var mergedObj = Object.assign(hike, matchingAvailabilityObj); //merge both objects
return mergedObj;
});
// now the data will have an array of merged object with properties from hike and availability objects
res.render("index", {data: data});
});
});
app.listen(port, function(){
console.log("Running");
});
There are two possible options for creating your page:
- cache answers from both apis
- proxy requests to them
You need chose what will you use.
For cache use setInterval
to store the answer into two variables objects each 5/60/N seconds.
For proxy use async/await approach and Promise.all
to continue work after your have both answers. In this case I propose change request
package to got
I would request you to read about Promise, and asynchronous function more for better understanding and solution.
For now this will work for you:
var hikes = {
url: "https://api./hikes",
headers: {
'Identifier': identifier
}
};
var availability = {
url: "https://api./hikes",
headers: {
'Identifier': identifier
}
};
app.get("/", function(req, res){
function callback(error, response, body){
if(!error && response.statusCode == 200){
var data = JSON.parse(body);
request(availability, (err, response, body) => {
if(!err && response.statusCode == 200){
var data2 = JSON.parse(body);
res.render("index", {data1: data, data2: data2});
}
});
}
}
request(hikes, callback);
});
index.ejs
:
<% data1["hikes"].forEach(function(hike){ %>
<p><%= hike.name %></p>
<% }) %>
<% data2["availability"].forEach(function(available){ %>
<p><%= available.slots %></p>
<p><%= available.available %></p>
<% }) %>
Better Solution
function apiCall (reqOps) {
return new Promise ( (resolve, reject) => {
request(reqOps, (err, res, body) => {
if(!error && response.statusCode == 200){
resolve( JSON.parse(body) );
}
reject(err);
});
});
}
var hikes = {
url: "https://api./hikes",
headers: {
'Identifier': identifier
}
};
var availability = {
url: "https://api./hikes",
headers: {
'Identifier': identifier
}
};
app.get("/", function(req, res){
let data1, data2;
apiCall(hikes)
.then( result => { // Result of first call
data1 = result;
return apiCall(availability);
})
.then( result => { // Result of second call
data2 = result;
res.render("index", {data1, data2});
})
.catch( err => {
console.log("Error occured in one of the API call: ", err);
});
});
// or with async-await
app.get("/", async function(req, res){
try {
let data1 = await apiCall(hikes);
let data2 = await apiCall(availability);
data1 = JSON.parse(data1);
data2 = JSON.parse(data2);
res.render("index", {hikes: data1.hikes, availability: data2.availability});
}
catch( err ) {
console.log("Error occured in one of the API call: ", err);
};
});
better index.ejs
:
<% if (hikes.length>0) { %>
<% if (availability.length>0) { %>
<% for (var i = 0; i < hikes.length; i++) { %>
<p><%= hikes[i].name %></p>
<p><%= availability[i].slots %></p>
<p><%= availability[i].available %></p>
<% } %>
<% } %>
<% } %>
Concept solved with multiple request functions. result, multiple desired api values are able to be accessed by global variable. node v10.15.1:
// require node packages
const express = require("express");
const bodyParser = require("body-parser");
const request = require("request");
//set app to use express package
const app = express();
//let app use body-parser package
app.use(bodyParser.urlencoded({extended:true}));
// let app set ejs as the view engine
app.set("view engine", "ejs");
// set view path
const view = __dirname + "/app/views/";
Solution starts here:
//assign api to constant
const btcUsd = "https://apiv2.bitcoinaverage./indices/global/ticker/BTCUSD";
const trxBtc = "https://apiv2.bitcoinaverage./indices/tokens/ticker/TRXBTC";
// function containing api parser
function tickers(){
request(btcUsd, function(error, response, body){
// handle errors if any
if(error){
console.log(error);
} else {
// parse json
let data = JSON.parse(body);
// get last price
btc = (data.last).toFixed(2);
console.log(btc);
}
});
request(trxBtc, function(error, response, body){
// handle errors if any
if(error){
console.log(error);
} else {
// parse json
let data = JSON.parse(body);
// get last price
usdConvert = (data.last) * btc;
trx = usdConvert.toFixed(4);
console.log(trx);
}
});
}
// function to trigger ticker function and set interval. (this is optional)
function getTickers(){
tickers();
// set interval
setInterval(tickers, 60000);
}
//activate getTickers function
getTickers();
The desired api values btc and trx can now be used by any rendered view that assign each value to an object key:
// render view
app.get("/", function(req, res){
res.render(views + "pages/index", {btcTicker: btc, trxTicker: trx});
});
In view:
<h1> <%= btcTicker %> </h1>
<br>
<h1> <%= trxTicker %> </h1>