Mongoose schema.post events broken in versions >= 4.8.0 - node.js

In a node app that uses mongoose, I defined some mongoose middleware hooks like this:
mongoose.model('MyModel').schema.post('save', function(document) {
// following is executed in 4.7.9 but not 4.8.0
console.log('saved');
});
These work perfectly well in mongoose versions up to and including 4.7.9 but if I update mongoose to 4.8.0 in my application, then with no other changes, these hooks fail to be invoked at all. There are no warnings or errors.
http://mongoosejs.com/docs/middleware.html suggests that the way to define these hooks hasn't changed. Is there something different I need to do in 4.8.0+ to retain this behaviour?

I'm pretty sure that you need to declare middleware before creating the model:
let MySchema = new mongoose.Schema(...);
MySchema.post('save', ...);
let MyModel = mongoose.model('MyModel', MySchema);
See this as well: https://github.com/Automattic/mongoose/issues/4971#issuecomment-279238187

Related

Load module in nodejs after setting DB data

My final goal is to have module.exports to contain some DB value which was loaded during app init
I have tried the following
module.exports = (async function() {
const cities = await internalUsage.aggregate(citiesPipeline);
console.log("HandleCities - Cities loaded")
return cities[0].cities;
})();
But that gives me a promise
Any idea on how to do that?
** Update **
For now I'm using globals, if anyone has a solution to keep this in the module, I would love to hear
Sadly this is not possible with only using module.exports. Due to the function being async the best thing you can get is a Promise when using vanilla NodeJS module.exports.
This is not that bad if you can await for it. module.exports works like a factory and only a single Promise will be generated no matter how many times you require it.
const citiesPromise = require('./XXXXX_XXXXX');
(async function(){
const cities = await citiesPromise();
...
});
An alternative solution is to abstract the NodeJS module using Dependency Injection (DI) with asynchronous factory support. This will allow you to export a Promise and get its value as an injection.
Not all NodeJS DI provides this abstraction for async values so be careful when selecting DI lib.
You can check InversifyJS although it encourage using TypeScript it has ES6 example as will https://github.com/inversify/InversifyJS/blob/master/wiki/basic_js_example.md.

Where data will be stored in Mongo DB by default, when we use mongoose with Express

I am new to Node JS technology. I have 3 basic doubts.
In my nodeJS application, I connected to mongodb using mongoose. But I did not mention any collection name. But data is getting saved when I sent data from Form as expected. I want to know that in which collection it will be stored by default. How to see the stored data.
how to mention specific collection name using mongoose if we want to save data in a particular collection.
3.Generally If we want to use any middleware in our app, we connect that
middleware using app.use() right? but in mongoose case, we do not add that
to app.use(). but still we can use the mongoose functionality.
could anyone please tell how it is possible.
Thanks a lot in advance.
How Mongoose interacts with MongoDB is described here.
It has this example:
var schema = new mongoose.Schema({ name: 'string', size: 'string' });
var Tank = mongoose.model('Tank', schema);
and mentions that
The first argument is the singular name of the collection your model is for. Mongoose automatically looks for the plural version of your model name. Thus, for the example above, the model Tank is for the tanks collection in the database.
model() takes a third argument where you can rename the collection:
var Tank = mongoose.model('Tank', schema, 'collectionname');
The collection gets made when model() is called.
app.use() is used for Express middleware. Mongoose isn't really that, which is why you're not using app.use() in this case.
This should probably be broken into multiple questions and you should probably show some code. That said, I'll take a crack at it.
Collection names are defined when you model your schema. So let's say you have:
const UserSchema = new Schema({
name: String
});
And then you later will tell mongoose to model it:
mongoose.model('User', UserSchema);
You'll have a collection called "users" in the database you're connecting to in your mongoose.connect() call.
Regarding middleware, Express middleware is specifically functions that you want to fire during the request/response cycle. You can still call code (e.g. mongoose) outside that cycle, and generally you'll connect to the database when the application starts and then read or save to it in either middleware or in your route handlers. For example, you might have a route like:
const User = mongoose.model('User');
app.get('/users', (req, res, next) => {
User.find({}, (err, users) => {
if (err) return next(err);
res.send(users);
});
});
In that case, you've got a route handler that calls mongoose through the User model previously defined.

Node require executes code twice for mongoose Schemas

I am having trouble with require executing my code twice. Working on a standard Express app I build Mongoose Schemas, each in it's own files and export them.
//user.js
const User = mongoose.model('User', userSchema)
module.exports = User
//In other files
const User = require('../models/User')
Now I use this in two places in my application and get an error saying that
Cannot overwrite `User` model once compiled.
So the code above is getting called twice as it is the only code right now creating a model. However I would expect Node to only execute it once since it is required in my code.
The really strange part is that checking out an earlier version from Git I get the same error and people working with me on this get the same error. So I have no more ideas where to look for solutions.
Found the solution now.
Turns out I required the module once as models/user and once as model/User which in the cache of require creates two separate modules.
There have been many discussion about this:
one issue
another issue
old PR
It seems that this is due to Windows resolving paths case insensitive while other systems resolve paths case sensitive and node therefore doing it sensitive.
And a new module of'cause gets executed. Simply requiring is both times spelled in lowercase solved the issue.
I think the problem is in "const" that you use to declare the variable "User".
Try to use "var" instead of .
//user.js
var User = mongoose.model('User', userSchema)
module.exports = User
//In other files
var User = require('../models/User')
P/S: This is link that clarify more about "const" and "var":
Const in javascript? When to use it and is it necessary
Hope it helpful for you !

Inject unique mongoose instance in module

My modules get a mongoose instance injected but now they all use the database that was set on the last mongoose.
For example my main module creates a lot of modules and then calls init on them.
var mongoose = require('mongoose');
//...
mongoose.connect(connString);//specific to finance
var finance = require('finance').init({db:mongoose});
Before I injected the mongoose instance the finance module required mongoose by itself and since it's in the node_modules it gets it's own mongoose. Now; no matter how many main modules I make and how many times I call require it'll always get the instance it got the first time.
Therefor all modules created will be connected to whatever is set by the last connect.
It is possible to use createConnection but still not sure how to inject mongoose, I tried:
var mongoose = require('mongoose');
//...
var c = mongoose.createConnection(connString);//specific to finance
mongoose.connection=c;
var finance = require('finance').init({db:mongoose});
Now I get an error like Cannot overwritefinancemodel once compiled.
Somehow it's very hard to get another instance of mongoose in the same module. This is funny because according to mongoose you should be able to use different connections for different models but since you need a mongoose instance to define a model then how do you inject it? Require keeps returning the same instance over and over again.
Tried the following but both didn't work.
console.log('deleting mongoose cache:',require.cache.mongoose=undefined);
//or this one
console.log('deleting mongoose cache:',delete require.cache.mongoose);
So the question is: how do I inject mongoose in my modules that have models that require a unique database? If main sets up the mongoose instance and connection to inject into the model then how do I prevent it from creating the same one over and over again?
If it's possible to create unique connections with createConnection then what do I inject into the modules? With this connection I can't create models, need a mongoose instance for that. If each model needs to invoke require to get it then mongoose is un injectable.
So mongoose uses a singleton pattern. When you do require("mongoose") you are getting the same instance of a constructor each time as seen at the bottom of mongoose/lib/index.js (source code link here). var mongoose = module.exports = exports = new Mongoose;
To get unique instances, use this pattern:
var singleton = require("mongoose")
var unique = new singleton.constructor();
//now use "unique" just as you would "mongoose"

How should mongoose's model.on('index', ...) be used?

In looking at the documentation for MongooseJS for MongoDB/Node.js, it appears that indexing may be specified on an object like so:
var MyModelSchema = new Schema({ name: String, index: true });
var MyModel = mongoose.model('MyModel', MyModelSchema);
MyModel.on('index', function()
{
doStuff();
});
Per the documentation, Mongoose will call ensureIndex() upon startup unless the "autoIndex" option is explicitly set to false on the schema. Once this is completed, the 'index' event will be emitted on the model, which would trigger the callback.
My understanding is that this allows a user of a model to ensure that all indexing has been completed prior to use of a model.
I believe I heard mention of doing this via app.configure, although I'm not sure how this could be done. Maybe there is another way to guarantee completion of this indexing operation prior to other parts of the application relying on the exported model?
How should this properly be used?
The 'index' event on your model is there to alert you to any errors that occurred in the model's ensureIndex calls. You don't need to delay use of the model until the event fires. That would get pretty messy.
I found it useful to add the following to the area used to configure the project's mongoose settings.
let mongoose = require('mongoose');
mongoose.Model.on('index', function(err) {
if (err) logger.error(err);
});

Resources