0

I'm building an application using Node/Express/MongoDB (first time with all these) that will let me pull data from the DB and display it on an Express page. This is what the GET request looks like:

var str = "";

app.route('/view-reports').get(function(req, res) {
  var cursor = collections.find({});

  cursor.each(function(err, item) {
    if (item != null) {
      console.log(str);
      str = str + "Substance: " + item.substance + "<br>";
    }
    if (err) {
      console.log(err);
    }
  });
  console.log(str);
  res.send(str);
  str = "";
});

I would expect this to return something like this:

Substance: a

Substance: b

Substance: c

However, the initial request does not return anything at all. The second request will return the above. If I enclose res.send(str) in an if conditional it simply will not load until a second request is made.

Community
  • 1
  • 1
  • 1
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Tsvetan Ganev Jun 04 '18 at 21:51
  • `cursor.each()` is asynchronous. That means it runs sometimes LATER, after your `res.send(str)`, thus you get the previous version of `str`. – jfriend00 Jun 04 '18 at 21:57
  • Possible duplicate of [Asynchronous issue within nodejs/mongodb code](https://stackoverflow.com/questions/39302920/asynchronous-issue-within-nodejs-mongodb-code) – Dan O Jun 05 '18 at 01:24

2 Answers2

0

cursor.each() is asynchronous. That means it runs sometimes LATER, after your res.send(str), thus you get the previous version of str. You need to collect all the data first and then send your response only when you have all the data.

If you want all the data, then you could use promises and .toArray() like this:

app.route('/view-reports').get(function(req, res) {
  collections.find({}).toArray().then(data => {
      let result = data.map(item => {
          return "Substance: " + item.substance + "<br>";
      }).join("");
      res.send(result);
  }).catch(err => {
      // database error
      console.log(err);
      res.sendStatus(500);
  });
});

Note: This also wisely gets rid of the str variable which was outside the scope of the request and thus could easily lead to a concurrency bug when multiple requests were in flight at the same time (from different users).

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • @gabrielmorris - Does this answer your question? If so, please indicate that to the community by clicking the checkmark to the left of the answer. That will also earn you some reputation points for following the proper procedure here. If not, then please indicate what is not yet answered. – jfriend00 Jun 26 '18 at 05:27
0

Create a router specifically for substances and use it in app. Instead of breaks, you can create a ul, also, that processing should happen on the front end. Separate your concerns. The server shouldn't have to worry about any rendering and etc. One purpose per process.

The routers can be created per resource. Create a router for substances, for cats, for dogs. Each individual router has it's own get post delete and puts that allow you to modify that resource. app can use all the routers at once.

app.use(catRouter); app.use(mooseRouter); app.use(platypusRouter);

const { Router } = require('express');
const createError = require('http-errors');
let substanceRouter = new Router();

function buildElement(arr)
{
  let start = '';
  arr.forEach(val => {
    if(!val) return;
    start += `Substance : ${val}<br>`;
  });
  return start;
}

subtanceRouter.get('/endpoint/whatever', function(req, res, next) {

  collectios.find({})
  
  .then(results => {
    if(!results) throw new Error('Resource Not Found');
    
    let output = buildElement(results);
    res.json(output);
    next();
  })
  
  .catch(err => next(createError(404, err.message)));
})

app.use(substanceRouter);

Alternately we can write :

let output = results
            .filter(sub => !!sub)
            .join('<br>');
res.json(output);

But be advised this will add memory overhead, generates a completely new array to house the results, consuming at worst, O(n) memory.

Eddie D
  • 1,120
  • 7
  • 16