13

I’m new to node and mongo after 15 years of VB6 and MySql. I’m sure this is not what my final program will use but I need to get a basic understanding of how to call a function in another module and get results back.

I want a module to have a function to open a DB, find in a collection and return the results. I may want to add a couple more functions in that module for other collections too. For now I need it as simple as possible, I can add error handlers, etc later. I been on this for days trying different methods, module.exports={… around the function and with out it, .send, return all with no luck. I understand it’s async so the program may have passed the display point before the data is there.

Here’s what I’ve tried with Mongo running a database of db1 with a collection of col1.

Db1.js
var MongoClient = require('mongodb').MongoClient;
module.exports = {
    FindinCol1 : function funk1(req, res) {
    MongoClient.connect("mongodb://localhost:27017/db1", function (err,db) {
            if (err) {
                return console.dir(err);
            }
            var collection = db.collection('col1');
            collection.find().toArray(function (err, items) {
                    console.log(items);
                   // res.send(items);
                }
            );
        });
    }
};


app.js
a=require('./db1');
b=a.FindinCol1();
console.log(b);

Console.log(items) works when the 'FindinCol1' calls but not console.log(b)(returns 'undefined') so I'm not getting the return or I'm pasted it by the time is returns. I’ve read dozens of post and watched dozens of videos but I'm still stuck at this point. Any help would be greatly appreciated.

MM1
  • 133
  • 1
  • 1
  • 5

2 Answers2

28

As mentioned in another answer, this code is asynchronous, you can't simply return the value you want down the chain of callbacks (nested functions). You need to expose some interface that lets you signal the calling code once you have the value desired (hence, calling them back, or callback).

There is a callback example provided in another answer, but there is an alternative option definitely worth exploring: promises.

Instead of a callback function you call with the desired results, the module returns a promise that can enter two states, fulfilled or rejected. The calling code waits for the promise to enter one of these two states, the appropriate function being called when it does. The module triggers the state change by resolveing or rejecting. Anyways, here is an example using promises:

Db1.js:

// db1.js
var MongoClient = require('mongodb').MongoClient;
/*
node.js has native support for promises in recent versions. 
If you are using an older version there are several libraries available: 
bluebird, rsvp, Q. I'll use rsvp here as I'm familiar with it.
*/
var Promise = require('rsvp').Promise;

module.exports = {
  FindinCol1: function() {
    return new Promise(function(resolve, reject) {
      MongoClient.connect('mongodb://localhost:27017/db1', function(err, db) {
        if (err) {
          reject(err);  
        } else {
          resolve(db);
        }        
      }
    }).then(function(db) {
      return new Promise(function(resolve, reject) {
        var collection = db.collection('col1');
        
        collection.find().toArray(function(err, items) {
          if (err) {
            reject(err);
          } else {
            console.log(items);
            resolve(items);
          }          
        });
      });
    });
  }
};


// app.js
var db = require('./db1');
    
db.FindinCol1().then(function(items) {
  console.info('The promise was fulfilled with items!', items);
}, function(err) {
  console.error('The promise was rejected', err, err.stack);
});

Now, more up to date versions of the node.js mongodb driver have native support for promises, you don't have to do any work to wrap callbacks in promises like above. This is a much better example if you are using an up to date driver:

// db1.js
var MongoClient = require('mongodb').MongoClient;
                       
module.exports = {
  FindinCol1: function() {
    return MongoClient.connect('mongodb://localhost:27017/db1').then(function(db) {
      var collection = db.collection('col1');
      
      return collection.find().toArray();
    }).then(function(items) {
      console.log(items);
      return items;
    });
  }
};


// app.js
var db = require('./db1');
    
db.FindinCol1().then(function(items) {
  console.info('The promise was fulfilled with items!', items);
}, function(err) {
  console.error('The promise was rejected', err, err.stack);
});

Promises provide an excellent method for asynchronous control flow, I highly recommend spending some time familiarizing yourself with them.

Emma
  • 2,012
  • 5
  • 21
  • 30
  • I must be running a newer version since your second option worked great. – MM1 Feb 07 '16 at 02:10
  • Thanks! Great Solutions! But in the second way, it does not seem that we need the second then. Is there a reason you put it in there? – Ramon Rahman Apr 13 '17 at 12:18
  • You are correct that the second `.then` is not required. The example the original author provided had a `console.log` that I matched for parity. – Emma Jul 06 '17 at 14:32
  • Do we have to explicitly close the database calling the close() method on the mongo database object? I don't see that happening in this code. Does it automatically implicitly close the database once the promise is fulfilled? – smartexpert Aug 11 '17 at 12:34
  • I too would like to know about the close. How do you close the database connection upon completion of the last example? – user1513388 Sep 28 '17 at 10:54
  • The db object exposes a [close](http://mongodb.github.io/node-mongodb-native/2.2/api/Db.html#close) method that returns a promise. _When_ you should close the connection will depend on your code. If this is a script (which you expect to exit after querying the database) then you can close the connection at the end of the promise chain. If this is a long running application (such as an express API) you should leave the database connection open for the duration of the process - it is not recommended to open and close a new connection for each query. Check out signal handlers. – Emma Sep 30 '17 at 21:06
  • 1
    Note that the examples provided in my answer are not structured the greatest. They were provided this way to best match the code in the original question. – Emma Sep 30 '17 at 21:10
6

Yes, this is an async code and with a return you will get the MongoClient object or nothing, based on where you put.

You should use a callback parameter:

module.exports = {
  FindinCol1 : function funk1(callback) {
    MongoClient.connect("mongodb://localhost:27017/db1", function (err,db) {
      if (err) {
        return console.dir(err);
      }
      var collection = db.collection('col1');
      collection.find().toArray(function (err, items) {
        console.log(items);       
        return callback(items);     
      });
    });
  }
};

Pass a callback function to FindinCol1:

a.FindinCol1(function(items) {
  console.log(items);
});

I suggest you to check this article: https://docs.nodejitsu.com/articles/getting-started/control-flow/what-are-callbacks

Gergo
  • 2,190
  • 22
  • 24