How to return Mongoose query results without callback

12,653

Use the Async/Await Syntax:

const findUser = async function (params) { 
    try {  return await User.findOne(params)
    } catch(err) { console.log(err) }
}

const userSteve = findUser({firstName: Steve})

Each time you need to use the information from a Query, use the await prefix within an async function. This will allow you to use asynchronous nature of a Mongoose query within synchronous code that needs that query result to continue.

For your code:

coreModule.findUser  = async function (userId) { 
    return await User.findOne({id:_userId})
}

coreModule.findPosts = async function(authorId) {
    return await Posts.find({postedBy: authorId})
}

const foundUser = coreModule.findUser(123);
const foundPosts = coreModule.findPosts(123);
res.send({user: foundUser, posts: foundPosts}) 

If you would like both queries to fire off simultaneously, you can use Promise.all()

coreModule.findUser  =  function (userId) { 
    return User.findOne({id:_userId})
}

coreModule.findPosts = function(authorId) {
    return Posts.find({postedBy: authorId})
}

const [foundUser, foundPosts] = 
   await Promise.all(
         [coreModule.findUser(123), coreModule.findPosts(123)]
   ); 

res.send({user: foundUser, posts: foundPosts})

If you have both Queries located at separate EndPoints in your API, you could also construct two fetch requests aimed at both endpoints and fire those off simultaneously on the Client-Side with Promise.all()

I hope this helped!

Edit: I have edited my post with this working example which I have tested in my API:

module.exports.test = async function(req, res, next) {
    const module1 = function(params) {
        return ShoppingCartModel.find({_id: params})
    }
    const module2 = function(params) {
        return User.find({_id: params})
    }
    const array = [module1('5a739dbc45424d2904faca5b'), module2('5a739dbc45524d2904faca5b')]
    const promise = await Promise.all(array)
    res.json(promise)
}

Some examples of incorrect returns:

Incorrect:

const array = [ShoppingCartModel.find({}), ShoppingCartModel.find({})]
// console.log(array) = Mongoose Query Constructors
const promise = Promise.all(array)
// console.log(promise) = Promise { <pending> }

Correct:

const array = [ShoppingCartModel.find({}), ShoppingCartModel.find({})]
const promise = await Promise.all(array)

Incorrect:

// Return full fledged promise from Mongoose query using .exec()
const array = [ShoppingCartModel.find({}).exec(), ShoppingCartModel.find({}).exec()]
// console.log(array) = [ Promise { <pending> }, Promise { <pending> } ]
const promise = Promise.all(array)
// console.log(promise) = Promise { <pending> }

Correct:

const array = [ShoppingCartModel.find({}).exec(), ShoppingCartModel.find({}).exec()]    
const promise = await Promise.all(array)

You must await the result of Promise.all, or else your function will blow through the function calls, send an empty JSON object back to the front-end, and console.log pending promises which are not given time to resolve

Share:
12,653

Related videos on Youtube

Isaac Krementsov
Author by

Isaac Krementsov

I am an enthusiastic web and mobile developer with a versatile skill set. At the beginning of my programming experience, I recognized that I was never done learning. Since then, I have become most familiar with using ExpressJS, MongoDB, Go, Java, HTML5, and more while continuously adding to that list. Whether you simply want to make your website known or need a real-time application, I am here to help.

Updated on September 14, 2022

Comments

  • Isaac Krementsov
    Isaac Krementsov over 1 year

    I have an app which is structured: index > routes > endpoints > core, where endpoints process requests to send a response and core accesses the database. The point of dividing controllers in to these two components was partially to make things work after a DB change and partially to simplify my code. The issue is that I can only get the results of a mongoose query function in my module via callback, which does not simplify my code at all.
    What I want:

    var user = coreModule.findUser({id:123}) //return user
    var posts = coreModule.findPosts({author:123}) //return posts array
    res.render("home", {user:user, posts:posts})
    

    By doing:

    //core module example
    findUser: function(params){
       User.findOne(params, function(err,docs){
          if(err){
             return err
          }else{
             return docs
          }
       }
    }
    

    But instead I have to use a mess of callback functions which defeat half of the purpose of core modules in the first place. I have seen that you can do this with SQL and Knex, but it will not work with mongoose. If what I am trying to do is impossible, is there a mongoose substitute that would be recommended in this case?