1

I'm connecting and making an insert with Node/MongoDB, but due to a scope issue, I can't access the connection from the function. Any idea how to make the 'db' variable global scope?

mongodb.connect("mongodb://localhost:27017/userDB", function(err, db) {

   if(!err) {
       console.log("We are connected");
   } else {
       console.log(err);
   }

});

function RegisterUser(user, pass) {
    var collection = db.collection('users');
    var docs = [{username:user}, {password: pass}];
    collection.insert(docs, {w:1}, function(err, result) {
        collection.find().toArray(function(err, items) {});
        socket.emit('message', items);
    });
}

/var/www/baseball/app.js:80
    var collection = db.collection('users');  <--db is not defined
                     ^
ReferenceError: db is not defined
at RegisterUser (/var/www/baseball/app.js:80:20)
at ParseData (/var/www/baseball/app.js:63:6)

3 Answers 3

1

In general, only make the connection once, probably as you app starts up. The mongo driver will handle pooling etc., at least, so I was told by experts from MongoLabs. Note that this is very different than what you might do in Java, etc... Save the db value returned by connect() somewhere, perhaps a global or in app, or in a commonly used one of your modules. Then use it as needed in RegisterUser, DeleteUser, etc.

Sign up to request clarification or add additional context in comments.

2 Comments

My bad, I just declared a global var global_db outside the callback. I thought I had tried that and didn't work but i tried it again and it did so thanks.
In the long run you probably need to do something more sophisticated as in Neil's answer, and you may not want a global, but at least you are started.
1

More of a node question really, but probably worth the tag since it gets asked a bit. So you say there is a scoping issue and you are right as the variable is local to the callback function on the .connect() method and is not visible anywhere else. One way is to dump all your logic inside that callback, so there is no scoping issue, but you probably don't want to do that.

Asking "how do I set a global", is also not really the right approach. Well not directly as there are general funny things about breaking up the "async" pattern of node. So a better approach is with some kind of "singleton" instance where you set the connection only once, but as that is global or can otherwise be "required" in for use in other areas of your application.

Here is one "trivial" approach to demonstrate, but there are many ways to do the same thing:

var async = require('async'),
    mongodb = require('mongodb'),
    MongoClient = mongodb.MongoClient;


var Model = (function() {

  var _db;
  var conlock;
  return {
    getDb: function(callback) {
      var err = null;
      if ( _db == null && conlock == null ) {
        conlock = 1;
        MongoClient.connect('mongodb://localhost/test',function(err,db) {
          _db = db;
          conlock == null;
          if (!err) {
            console.log("Connected")
          }
          callback(err,_db);
        });
      } else if ( conlock != null ) {
        var count = 0;
        async.whilst(
          function() { return ( _db == null ) && (count < 5) },
          function(callback) {
            count++
            setTimeout(callback,500);
          },
          function(err) {
            if ( count == 5 )
              err = new Error("connect wait exceeded");
            callback(err,_db);
          }
        );
      } else {
        callback(err,_db);
      }
    }
  };

})();


async.parallel(
  [
    function(callback) {
      console.log("call model");
      Model.getDb(function(err,db) {
        if (err) throw err;
        if (db != undefined)
          console.log("db is defined");
        callback();
      });
    },
    function(callback) {
      console.log("call model again");
      Model.getDb(function(err,db) {
        if (err) throw err;
        if (db != undefined)
          console.log("db is defined here as well");
        callback();
      })
    }
  ],
  function(err) {
    Model.getDb(function(err,db) {
      db.close();
    });
  }
);

So out little "Model" object here has a single method in .getDb(), and it also maintains a private variable holding the _db connection once it has been established. The basic logic on that method is to see if _db is defined and where it not then establish a connection with the driver. On the connection callback the _db variable is then set.

The other thing here is the method itself accepts a "callback" so this is how you use it later, where either an error or the current connection will be returned.

The last part is just a demonstration of two functions to be implemented in code. Where in the first call the call to connect to the database is made before following into the callback function provided.

The next time we call though, the connection is already set in the private variable, so that data is merely returned and you don't establish a connection again.

There are various ways to implement this sort of thing, but that is the basic logic pattern to follow. There are many other "helper" implementations that wrap the MongoDB driver to make these sort of things simple, as well as managing connection pools and ensuring the connection is up as well for you, so it may be well worth looking at these even if you are still insistent on doing all the work yourself from the lower level driver base.

7 Comments

See comment in answer #1. I appreciate the detailed effort, but it was as simple as declaring a global (window) variable outside the callback and setting the local db object equal to that global variable. Thanks.
@TheHawk I pitty the time when your production application receives a hit on an URL directly after startup when the database is not connected yet then. With a few seconds delay, the global would be declared, but otherwise it will not. This is the point you are missing.
@Neil valid point, but if they get 2 hits early on I think your code will open 2 connections, right? Seems like there should be some standard "recipe" to do this.
@user949300 No it does not create two connections which is why the listing is a runnable example to show that it does not. So it's meant to be tried to see the results yourself. Anything expecting a declared variable outside of a callback that can initiate a connection will certainly fail.
@Neil Lunn, your example should be asych.parallel to test my concerns.
|
0

First of all, you are only going to be registering users once you have a connection, so do what ever work you need to do in there... so call RegisterUser from within the connection scope. If you want to use the db object within that function you will need to pass in the parameters as db

RegisterUsers(db, user, pass)

you may then use db within the function

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.