简体   繁体   中英

Express.js GET request not returning data on first call

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.

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).

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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