o9339013
o9339013

Reputation: 11

Nodejs wait for query

I'm using Nodejs with MongoDB(mongoose along with express).

Since I don't trust the user data, I need to verify it from the database.

input data:

{
 "id": "someid",
 "nottrusteddata": [ {"id": "1"}, {"id" :"2"}]
}

In my function, I'm verifying the data:

router.post("/validate", (req, res,next) =>{
  let validated_data = validate_data(req); 
  console.log(JSON.stringify(validated_data));
  const mydata = new Mydata({
     id: req.body.id,
     lst : validated_data   
  });
  console.log("mydata: " + JSON.stringify(mydata));
  /* Some Usefull stuff is here */
  res.status(200).json();
}

function validate_data(req){
 let validated_data = []
 for(let i = 0; i < req.body.nottrusteddata.length; i++)
 {
   Databaseobject.findOne({'id': req.body.nottrusteddata[i].id})
   .exec()
   .then(dbobject =>{
     if(dbobject) // not undefined, it exists in the database
     {
       // Some logic with the object returned from the database
       let tmp_object = {};
       tmpobject.id = dbobject.id;
       // Append it to the list, so that the upper function can use it
       validated_data.push(tmp_object);
     }
   })
 }
 return validated_data;
}

The desired output should contain the correct information coming from the database, however, due to the async nature of the nodejs, validated_data returns null.

I have also tried using Promise. I couldn't succeed it.

const validate_data = function(req){
  return new Promise(function(resolve,reject){

     let validated_data = []
     for(let i = 0; i < req.body.nottrusteddata.length; i++)
     {
       Databaseobject.findOne({'id': req.body.nottrusteddata[i].id})
       .exec()
       .then(dbobject =>{
         if(dbobject) // not undefined, it exists in the database
         {
           let tmp_object = {};
           tmpobject.id = dbobject.id;
           validated_data.push(tmp_object);
         }
       })
     }
     resolve(validated_data);
  }
}

What am I doing wrong? How can I wait for the database query to finish, then execute the main part? If there is only one validation, I could've used .then(). However, the list might have contained many elements and I need to wait for all of them to be verified.

Upvotes: 1

Views: 393

Answers (1)

Steve Holgado
Steve Holgado

Reputation: 12071

Your Databaseobject.findOne() calls are asynchronous so your promise will resolve before any of them complete.

You can make use of Promise.all to wait until all of your promises resolve.

Hopefully, this will work for you:

router.post("/validate", (req, res) => {

  validate_data(req.body.nottrusteddata)
    .then(validated_data => {
      const mydata = new Mydata({
        id: req.body.id,
        lst: validated_data   
      })

      // Some useful stuff is here

      res.status(200).json()
    })
    .catch(err => {
      // Handle error
    })

}

function validate_data(nottrusteddata) {

  // Create array of pending promises
  const promises = nottrusteddata
    .map(item => {
      return Databaseobject
        .findOne({ 'id': item.id })
        .exec()
    })

  // Wait for all promises to resolve
  return Promise.all(promises)
    .then(docs => {
      return docs
        .filter(dbobject => dbobject) // Filter out undefined
        .map(dbobject => {
          return { id: dbobject.id }
        })
    })

}

If you want, you could also use async-await here:

router.post("/validate", async (req, res) => {

  try {
    const validated_data = await validate_data(req.body.nottrusteddata)

    const mydata = new Mydata({
      id: req.body.id,
      lst: validated_data   
    })

    // Some useful stuff is here

    res.status(200).json()
  }

  catch(err) {
    // Handle error
  }

})

Upvotes: 1

Related Questions