0

So I am making a kind of API middleware for my company that will grab information from the NOAA API and then store in in my database. It does more then but that a separate part. I have set it up so that it works it will get the information and store it in my sql database perfectly The issue is the information I get is based off of zipcode. One request is the information for one zipcode. I need to be able to 'loop" through a list of zipcode one at a time and store the information in the database. I am not sure how to properly get it to work. I have tested a couple of ways but have not been able to get it to work so if someone can get me pointed in the right direction it would be appreciated.

Sorry in advance my code is not cleaned up. Everything below apiRequest.end() has little function for the question. I keep it for context.

let mysql = require('mysql');
let config = require('./config.js');
var https = require("https");
var express = require("express");
var app = express();
const port = 3000;
var fs= require('fs');
var csv = require('fast-csv');

//last test
//array will replace this zip variable 
let zip = '90012';
api(zip);

function api(zips){
//All of the parts for building the get requests url
app.get("/", function(req, response) {
  var apiKey = "gPaEVizejLlbRVbXexyWtXYkfkWkoBhd";
  let webapi = 'https://www.ncdc.noaa.gov/cdo-web/api/v2/data?';
  let datasetid="datasetid=GHCND";
  let datatypeid="&datatypeid=TMAX";
  let location="&locationid=ZIP:";
  const zipcode = zips;
  let startdate="&startdate=2019-01-01";
  let enddate="&enddate=2020-01-01";
  let units = "&units=standard";
  let limit="&limit=1000";
  let url = webapi + datasetid + datatypeid + location + zipcode + startdate + enddate + units + limit;
  var options = {
    port: 443,
    method: "GET",
    headers: {
      "token": apiKey
    }
  };

  let data = "";
//request to grab from NOAA api
  let apiRequest = https.request(url, options, function(res) {
    console.log("Connected");
    //grabing all data
    res.on("data", chunk => {
      data += chunk;
    });

    res.on("end", () => {
      console.log("data collected");
      //Format JSON data
      response.send(JSON.parse(data));
      var getData = JSON.parse(data);
      if(isEmpty(getData)){
        emptyCorrect();
      }
      dataFormat(getData);
      });
  });
  apiRequest.end();
});

//fix format for date Can add more formating if needed here
function dataFormat(formData){
  for(x in formData.results){
    let date = formData.results[x].date;
    formData.results[x].date = date.slice(0,10);
    }
  jsonToSQL(formData.results);
}

//test function is going to be used for inserting the zip
function test(){
  var content = "";
  console.log("your test worked see ***************");
  return "92507";
}


//function to add grabed JSON data into the SQL database
function jsonToSQL(datafin){
  var zipcode = zips;
  let connection = mysql.createConnection(config);

  // insert statment
  let stmt = `INSERT INTO test1(ZIPCODE,DATE, TEMP)  VALUES ?  `;
  let values = [];
  for(let x in datafin){
    values.push([zipcode,datafin[x].date,datafin[x].value]);
      }

// execute the insert statment
connection.query(stmt, [values], (err, results, fields) => {
  if (err) {
    return console.error("error");
  }
  // get inserted rows
  console.log('Row inserted:' + results.affectedRows);
});

// close the database connection
connection.end();
}


function emptyCorrect(){
  console.log("Eror correction");
  var zipcode = zips;
  let connection = mysql.createConnection(config);

  // insert statment
  let stmt = `INSERT INTO test1(ZIPCODE,DATE, TEMP)  VALUES ?  `;
  let valueE = [];
    valueE.push([zipcode,"0","No Data"]);

  // execute the insert statment
  connection.query(stmt, [valueE], (err, results, fields) => {
    if (err) {
      return console.error("error");
    }
    // get inserted rows
    console.log('Row inserted:' + results.affectedRows);
  });

  // close the database connection
  connection.end();
}

function isEmpty(obj) {
    for(var key in obj) {
        if(obj.hasOwnProperty(key))
            return false;
    }
    return true;
}

app.listen(port, () => console.log(`Example app listening on port ${port}!`))
}
  • Hi there! First of, you might want to remove the value of your `apiKey` - I am assuming that it acts as credentials for the NOAA API. Secondly, it is a bit unclear to me what you are asking. Could you elaborate what your intended behavior is for your code exactly? E.g. should your API resource be able to receive a zipcode from the caller? Or do you want it to loop through an array of predetermined (hard-coded) zipcodes? – abondoa Apr 10 '20 at 22:00
  • So everything below apiRequest.end() has no function for the question. Basicly I need to change the zipcode in the url for the get call sent to NOAA. I have a table that will create an array of all the zipcodes I need to get the information for. I left that out for simplification until I can properly implement this. I need to make a call for each zipcode in the array. The issue is I can not figure out how to make it so that I can make multiple calls for each zipcode in order like a loop. – Anthony Wright Apr 10 '20 at 22:53
  • I added a comment at the top to better explain why it is there . I tried making a loop here but it fails because the previous function is still running its listen to port. Which I would love to get rid off but don't know enough on how to. – Anthony Wright Apr 10 '20 at 23:27

1 Answers1

0

As I understand your problem can roughly be summarized as "How to loop through asynchronous evaluations in Nodejs".

There are some options for you. I would recommend wrapping call to the NOAA API with a promise and then chain those promises. This can be done as follows:

app.get('/', async function(req, response) {
  var apiKey = 'some value';
  let webapi = 'https://www.ncdc.noaa.gov/cdo-web/api/v2/data?';
  let datasetid = 'datasetid=GHCND';
  let datatypeid = '&datatypeid=TMAX';
  let location = '&locationid=ZIP:';
  let startdate = '&startdate=2019-01-01';
  let enddate = '&enddate=2020-01-01';
  let units = '&units=standard';
  let limit = '&limit=1000';
  var options = {
    port: 443,
    method: 'GET',
    headers: {
      token: apiKey
    }
  };

  const zipCodes = ['90012', '90013']; // Place a call to your function for fetching zip codes here
  let datas = [];
  prom = Promise.resolve();
  zipCodes.forEach(zipcode => {
    prom = prom.then(() => 
      new Promise((resolve, reject) => {
        let url =
          webapi +
          datasetid +
          datatypeid +
          location +
          zipcode +
          startdate +
          enddate +
          units +
          limit;
        let apiRequest = https.request(url, options, function(res) {
          console.log('Connected');
          let data = '';
          res.on('data', chunk => {
            data += chunk;
          });

          res.on('end', () => {
            console.log('data collected for zip ' + zipcode);
            datas.push(data);
            resolve();
          });
        });
        apiRequest.end();
      })
    );
  });
  prom.then(() => {
    // All requests have now been handled sequentially
    response.send(/* You'll need to figure out what to do here */);
  });
});

An alternative is to use something like the async library for dealing with sequentially calling callbacks. The async library (https://github.com/caolan/async) describes itself as:

Async is a utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript.

See e.g. Node.js: How do you handle callbacks in a loop? for a similar problem (not with regards to callign an API, but dealing with asynchronous function in a loop).

abondoa
  • 1,613
  • 13
  • 23
  • The promises I think are not running in order. I get ``` prom.then(() => { // All requests have now been handled sequentially response.send(datas); });``` To be blank and runs before the other promise when i run a console .log – Anthony Wright Apr 13 '20 at 20:38
  • You are right - I missed the `prom = prom.then(() =>` part. See updated code. – abondoa Apr 13 '20 at 20:39