Joel Divekar Joel Divekar - 2 months ago 11
Node.js Question

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();
};

rsp rsp
Answer

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: