简体   繁体   English

如何使用NodeJ中的请求进行多个API调用?

[英]How to make multiple API calls using request in NodeJs?

Creating a simple Node.Js app where need to display data from two APIs, where both APIs returns multiple objects with an ID. 创建一个简单的Node.Js应用程序, Node.Js应用程序需要显示来自两个API的数据,其中两个API均返回具有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的数据,并且需要以某种方式基于ID从这两个API提取数据。

API 1 response looks like this: API 1响应如下所示:

    {
    "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: API 2响应如下所示:

{
    "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". 需要同时提取两个API,获取数据并在页面上呈现以显示“名称”,“插槽”和“可用”。

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. 到目前为止,我们设法提取了一个API,并将数据传递到呈现的index.ejs页面,但是我不确定应该如何提取第二个API,以及某些如何获取数据。

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.com/hikes",
    headers: {
      'Identifier': identifier
    }
  };

  var availability = {
    url: "https://api.com/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: 我现在在index.ejs中创建了一个简单的循环来打印名称:

<% data["hikes"].forEach(function(hike){ %>

    <p><%= hike.name %></p>

<% }) %>

Any ideas on how to solve this? 关于如何解决这个问题的任何想法?

Thanks! 谢谢!

If 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. 如果我理解正确,我假设您正在尝试从两个API中获取数据,并希望根据对象ID将数据合并到单个对象数组中并将其传递给视图。 if this is the case then you can use https://www.npmjs.com/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. 如果是这种情况,则可以使用https://www.npmjs.com/package/async从两个API并行获取数据,然后将数据合并到一个对象数组中并将其传递给视图。 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.com/hikes",
  headers: {
   'Identifier': identifier
  }
};

var availability = {
  url: "https://api.com/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 缓存两个API的答案
  • 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. 对于缓存,请使用setInterval将答案存储在每个5/60 / N秒的两个变量对象中。

For proxy use async/await approach and Promise.all to continue work after your have both answers. 对于代理,请使用async / await方法和Promise.all ,在得到两个答案后继续工作。 In this case I propose change request package to got 在这种情况下,我建议改变request

Thank you for your reply! 谢谢您的回复!

Do anyone have any example code on a similar case? 有人在类似案例中有任何示例代码吗? Of the two above I think proxy would be the best fit for my app. 在上述两个方面中,我认为代理将是最适合我的应用程序的。

How would you fetch the data together and pass to the rendered page? 您将如何一起获取数据并传递到呈现的页面? Create a new array or similar where you combine the data? 在合并数据的地方创建一个新的数组或类似的数组?

I would request you to read about Promise, and asynchronous function more for better understanding and solution. 我希望您阅读有关Promise和异步功能的更多信息,以便更好地理解和解决。

For now this will work for you: 现在,这将为您工作:

var hikes = {
    url: "https://api.com/hikes",
    headers: {
      'Identifier': identifier
    }
};

var availability = {
    url: "https://api.com/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 : 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.com/hikes",
    headers: {
      'Identifier': identifier
    }
};

var availability = {
    url: "https://api.com/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 : 更好的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. 结果,可以通过全局变量访问多个所需的api值。 node v10.15.1: 节点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.com/indices/global/ticker/BTCUSD";
const trxBtc = "https://apiv2.bitcoinaverage.com/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: 现在,任何将每个值分配给对象键的渲染视图都可以使用所需的api值btc和trx:

// 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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM