0

I have created a nodejs express application using Express Generator.

In its route index.js I'm trying to make a rest api call using default HTTP module in the standard library https://nodejs.org/api/http.html#http_http_get_options_callback (I do not want to install external dependencies)and that would provide the values for title and welcome text in the view. Below is the code for index.js.

var express = require('express');
var router = express.Router();
const http = require('http');


/* GET home page. */
router.get('/', function(req, res, next) {
  let titletext = '';
  let wtext = '';
  http.get('http://localhost:5000/api/values', (apires) => {
    const { statusCode } = apires;
    const contentType = apires.headers['content-type'];

    let error;
    if (statusCode !== 200) {
      error = new Error('Request Failed.\n' +
                        `Status Code: ${statusCode}`);
    } else if (!/^application\/json/.test(contentType)) {
      error = new Error('Invalid content-type.\n' +
                        `Expected application/json but received ${contentType}`);
    }
    if (error) {
      console.error(error.message);
      // Consume response data to free up memory
      apires.resume();
      return;
    }

    apires.setEncoding('utf8');
    let rawData = '';
    apires.on('data', (chunk) => { rawData += chunk; });
    apires.on('end', () => {
      try {
        const parsedData = JSON.parse(rawData);
        console.log(parsedData);
        titletext=parsedData[1];
        wtext=parsedData[0];
      } catch (e) {
        console.error(e.message);
      }
    });
  }).on('error', (e) => {
    console.error(`Got error: ${e.message}`);
  });


  res.render('index', { title: titletext, data:wtext });
});

module.exports = router;

But it does not seem to be working and titletext and wtest were coming as empty strings. So, I added three breakpoints. One just after the get call, second after get call response comes and the last on the page response render call(res.render) at the very end.

Now when I run, I find that the get call breakpoint gets called,then the page response render call breakpoint gets called and finally the get call response breakpoint gets called, the data comes from the rest api but by then the page render call is already over and so the data from api call does not reach the view.

Any ideas how to solve this is sincerely appreciated

Arnab
  • 2,324
  • 6
  • 36
  • 60
  • @Quentin The question you have linked this question to be duplicate of is a completely different question and not even a node.js one – Arnab Aug 12 '19 at 15:29
  • It's a general JavaScript problem, not a Node.js specific problem. This question is about a using a value inside an async callback *outside* of that callback … and so is the duplicate. It's the same problem. – Quentin Aug 12 '19 at 15:30

1 Answers1

0

Http request call is async, so if you put res.render at the end of GET handler, it will render empty data before request returns response.

Following the document, you should parse response's body and render page from end event handler, like:

router.get('/', function(req, res, next) {
  let titletext = '';
  let wtext = '';
  http.get('http://localhost:5000/api/values', (apires) => {
    const { statusCode } = apires;
    const contentType = apires.headers['content-type'];

    let error;
    if (statusCode !== 200) {
      error = new Error('Request Failed.\n' +
                        `Status Code: ${statusCode}`);
    } else if (!/^application\/json/.test(contentType)) {
      error = new Error('Invalid content-type.\n' +
                        `Expected application/json but received ${contentType}`);
    }
    if (error) {
      console.error(error.message);
      // Consume response data to free up memory
      apires.resume();
      return;
    }

    apires.setEncoding('utf8');
    let rawData = '';
    apires.on('data', (chunk) => { rawData += chunk; });
    apires.on('end', () => {
      try {
        const parsedData = JSON.parse(rawData);
        console.log(parsedData);
        titletext=parsedData[1];
        wtext=parsedData[0];

        // Render page here
        res.render('index', { title: titletext, data:wtext });
      } catch (e) {
        console.error(e.message);
      }
    });
  }).on('error', (e) => {
    console.error(`Got error: ${e.message}`);
  });


  // Not here
});
ThanhPhanLe
  • 1,315
  • 3
  • 14
  • 25
  • Do not understand..newbie in node.js.. your code is the same as a part of my code... Can you please elaborate and provide the full page code. Thanks – Arnab Aug 12 '19 at 14:27
  • 1
    I updated the whole router code as you need, the point here is you only render index page when the request returned response, and in this context, you need to render index page right after parsing body in `end` event handler – ThanhPhanLe Aug 12 '19 at 14:32