6

I am trying to connect to my Mongo database which is situated on the machine as my Meteor app. Here are two files in my app:

a.js:

if (Meteor.isServer) {

    var database = new MongoInternals.RemoteCollectionDriver("mongodb://127.0.0.1:3001/meteor");
    Boxes = new Mongo.Collection("boxes", { _driver: database });
    Meteor.publish('boxes', function() {
        return Boxes.find(); 
    }); 
}

b.js:

if (Meteor.isClient) {
    Meteor.subscribe('boxes');
    Template.homeCanvasTpl.helpers({
        boxes: function () {
            return Boxes.find({});
        }
    });
}

But I keep getting a "Exception in template helper: ReferenceError: Boxes is not defined" error - any ideas?

JoeTidee
  • 24,754
  • 25
  • 104
  • 149
  • You haven't defined the collection on the client. You have to define it on both the server and the client. I don't think that you have to pass any second parameters when defining it on the client because you'd still be using your default connection to the server. – Jay Feb 16 '16 at 14:50

3 Answers3

19

How can you connect to a MongoDB with Meteor?

Scenario A: Use the built-in DB as default

This is much simpler than what you did. When you run meteor you actually start a DB with the Meteor server, where Meteor listens on port 3000 and the database on port 3001. The Meteor app is already connected to this database at port 3001 and uses a db named meteor. There is no need whatsoever to fall back to MongoInternals.RemoteCollectionDriver. Just remove that code and change things to:

 Boxes = new Mongo.Collection("boxes"); // use default MongoDB connection

Scenario B: Use a different DB as default

Using the MONGO_URL environment variable you can set the connection string to a MongoDB when starting the Meteor server. Instead of the local port 3001 database or an unauthenticated connection you can specify exactly where and how to connect. Start your Meteor server like this:

$ MONGO_URL=mongodb://user:password@localhost:27017/meteor meteor

You can also leave out the user:password@ part of the command if no authentication is needed.

Scenario C: Connect to a second (3rd, etc) DB from the same Meteor app

Now we need to use MongoInternals.RemoteCollectionDriver. If you wish to use another database that is not the default DB defined upon starting the Meteor server you should use your approach.

var database = new MongoInternals.RemoteCollectionDriver('mongodb://user:password@localhost:27017/meteor');
var numberOfDocs = database.open('boxes').find().count();

Bonus: Why should you not use MongoInternals with Mongo.Collection?

As the docs indicate you should not pass any Mongo connection to the new Mongo.Collection() command, but only a connection to another Meteor instance. That means, if you use DDP.connect to connect to a different server you can use your code - but you shouldn't mix the MongoInternals with Mongo.Collection - they don't work well together.

Stephan
  • 1,279
  • 8
  • 16
  • What would the easiest way to declare a remote collection be then? Does something along the lines of `var Boxes = database.open('boxes')` work? – jimmiebtlr Mar 15 '15 at 14:12
  • It's not that simple because there is a lot of "magic" going on in a Collection that goes beyond the simple database collection (i.e. reactivity, tracking the oplog, etc). If you can do without these characteristics Scenario C gives you quick and easy access to a remote DB coll, it is just not reactive. – Stephan Mar 15 '15 at 15:55
  • Be AWARE: The collections may look empty, you would need to make an insert in order to see the data. – Ruben Aug 08 '17 at 05:03
1

Based on feedback from saimeunt in the comments above, s/he pointed out that MongoInternals is unavailable to the client portion of a Meteor app. Therefore, the solution was to add in the line "Boxes = new Mongo.Collection("boxes");" to the client logic - here was the final working solution:

a.js:

if (Meteor.isServer) {

    var database = new MongoInternals.RemoteCollectionDriver("mongodb://127.0.0.1:3001/meteor");
    Boxes = new Mongo.Collection("boxes", { _driver: database });
    Meteor.publish('boxes', function() {
        return Boxes.find(); 
    }); 
}

b.js

if (Meteor.isClient) {
    Boxes = new Mongo.Collection("boxes");
    Meteor.subscribe('boxes');
    Template.homeCanvasTpl.helpers({
        boxes: function () {
            return Boxes.find({});
        }
    });
}
JoeTidee
  • 24,754
  • 25
  • 104
  • 149
0

Meteor has 2 different environment : the server environment running on Node.JS and the client environment running in browsers.

In your code you declare the Boxes Mongo collection only in the server environment, you need to take this declaration out of the Meteor.isServer condition (and BTW don't use these, separate your code in server/, client/ and lib/ directories).

Also, not sure if you need to connect to your MongoDB this way, maybe you should look into the MONGO_URL environment variable it probably already does what you need ? (provide a mongo connection URL to a distant (or local) Mongo server).

saimeunt
  • 22,666
  • 2
  • 56
  • 61
  • I moved the declaration out of the Meteor.isServer condition (above it) and now I get this error: "MongoInternals is not defined" - any ideas? p.s. I am trying to use this method because it was recommended on Stack Overflow elsewhere as a method that supersedes the MONGO_URL one. – JoeTidee Feb 28 '15 at 23:51
  • Then try using 2 declarations, one in the server using the `MongoInternals` stuff and another one in the client without. – saimeunt Mar 01 '15 at 01:36
  • Using 2 declarations contradicts the documentation of having one declaration outside of any isClient/isServer block, so that the resulting variable is globally accessible across the application. – JoeTidee Mar 01 '15 at 01:56
  • 1
    Read my answer again : there are 2 environments in Meteor, so a variable is never truly available across the application, a single declaration always spawns 2 different version of the variable corresponding to the 2 different environments. In your specific case, you can't declare the collection seamlessly on both environments because `MongoInternals` is unavailable on client, so you have to rely on 2 separate declarations. – saimeunt Mar 01 '15 at 02:05
  • I read your answer again and couldn't see anything about you saying MongoInternals was not available to the client. Now you have mentioned this, could you point myself and others in the direction of documentation on this? – JoeTidee Mar 01 '15 at 02:13
  • There's no documentation for `MongoInternals` because I guess this is supposed to be an advanced MongoDB configuration API only available on the server, on the client there is only `minimongo` which is an in-memory subset of the MongoDB API. – saimeunt Mar 01 '15 at 02:48