Mongoose - add global method to all models - node.js

Simple question:
How can I add static methods to my models in Mongoose, that applies to every model instead of just one?

So you have one static method that (eg) your User, Blog, Comment, and Alert models all share without any differences in implementation?
The de facto way to apply behavior to multiple different models in Mongoose is through plugins, and you can do a global plugin. I'll stick to traditional syntax, but if you want to use ES6 imports and exports feel free.
// ./models/plugins/echo.js
module.exports = function echoPlugin(schema, options) {
schema.statics.echo = function(){ console.log('Echo'); }
}
That defines a plugin which could be applied to a single schema like so:
userSchema.plugin(require('./plugins/echo'));
Or alternatively to all models in your project like so:
// somewhere in your app startup code
var mongoose = require('mongoose');
var echoPlugin = require('./models/plugins/echo');
mongoose.plugin(echoPlugin);

Related

How to set up references and import external modules as top-level objects in Typescript in Node.js

So this is a Typescript file structure and naming question. I'm just starting to learn about Typescript classes and modules and I have a few questions on how to structure model logic across multiple files.
Say I have a small rest api written in node.js with User and Photo models, and it's a simple api that lets users register and upload a photo.
Assuming this file structure:
-app
-models
-user.js
-photo.js
-controller.js
In my controller is it better to use a reference path declaration or external modules when referencing models? Does this overlap with just using a standard node.js require statement?
If I use an import statement to load the models as an external module, can I change the below example to move the export to the top-level so that I can just new up a User() rather than models.User().
for example, the models, and anotherModel seems a bit redundant - and I don't want to combine all the models into one file.
controller.js
import models = require('./models/user');
import anotherModel = require('./models/photo');
var newUser = new models.User();
var newMoment = new anotherModel.Moment();
user.js
export class User {
name:string
}
photo.js
export class Photo {
url:string
}
You can create a model / index.ts and re-export all the models from that file for easier consumption. This is conventional node.js paradigm.

OOP with MVC using Mongoose & Express.js

I'm creating an Express.js app in which I want to use the MVC pattern and Mongoose for the document mapping to a MongoDB database. I've created a folder for models and I want to derive everything from (Javascript's version of) abstract classes for better code organization.
I'm confused about what the best way is to organize the abstract classes and set default values that each instance of the models should be. For example, one way is to use Mongoose Schemas for abstract classes, and then use Mongoose models for the models themselves:
Feline.js:
var mongoose = require('mongoose');
var Feline = mongoose.Schema({
size: 'Number'
});
Feline.methods.getSize = function () {
return this.size;
}
module.exports = Feline;
HouseCat.js:
var mongoose = require('mongoose')
, FelineSchema = require('./Feline.js');
var HouseCatModel = mongoose.model('HouseCat', FelineSchema)
, HouseCat = new HouseCatModel({
size: 1 //Domesticated cats are small
});
module.exports = HouseCat;
There are a few problems with this design. For one, I would think there must be a better way to set specific properties for the each model without instantiating a new model object each time the client wants to create a new instance of a model type. For another, using this scheme, Mongoose has to be required in every model file, and the code is custom-tailored to use mongoose, which means it will be difficult to switch to another ODM if we want to do that in the future.
Is there any better way of coding this? And is there any design pattern which is easy enough to implement in Node that will allow for easy changing of the ODM?
As mongoose is specific to mongodb, this will be a hard task to abstract its behaviour.
The easiest way to do it is to set an interface for all ODMs and use an adapter pattern where mongoose is an "adaptee". Then, you can use a module providing some dependency injection to replace the used ODM.
As it is a really long task, I cannot give you some code. Moreover, it may be a pain to implement that kind of thing in javascript because it does not provide strong OOP natively. However, I can suggest you to take a look at some frameworks which can help you to do that like Danf for instance which provides a strong OOP paradigm with interfaces, classes, inheritance and a powerful dependency injection.

Is possible to add to already loaded schema some new static functions from some file?

Is possible to add to already loaded schema some new static functions from some file ?
I tried like to extends schema in mongoose
_ = require('lodash');
var schema = new mongoose.Schema(model.schema, model.options);
_.extend(schema, require('../extension/person_statics'));
but I get error
throw new MongooseError.OverwriteModelError(name);
^
OverwriteModelError: Cannot overwrite `Person` model once compiled.
Sorry, it is not possible to modify or extend a schema that was compiled.
The best practices to create a mongoose schema, is first of all initialize all your models with its specific schemas, and then compile all together.
This workflow may be difficult some times, I recommend you that use a framework for your models.
Check this framework for express and mongoose:
https://github.com/codexar/rode
The models are compiled on demand to prevent your problem.
I hope I have helped!

Mongoose: Using model or schema provided by a module from npm/node_modules

Say I have an application that defines a few models and creates the global Mongoose connection (mongoose.connect(...)). The app also relies on some models defined by a module in the node_modules directory. The problem that I'm running into is that the application and the separate module do not share the same global connection object.
Okay, fine. Rather than having the module export a model it can just export the schema and the main application can register it with the connection. In the app there would be something like:
var SomeSchema = require('somemodule').SomeSchema;
mongoose.model('SomeModel', SomeSchema);
Unfortunately this isn't working either. When the model is registered, Mongoose is checking whether the given schema is an instance of mongoose.Schema (here). When SomeSchema was defined (in the separate module) it creates the schema with new mongoose.Schema({ ... }) where mongoose is required from the module's local dependency tree. When the application registers the schema it uses the mongoose required from the application's dependencies. Since the two copies of mongoose are not the same object the given schema is not an instance of mongoose.Schema and an error is thrown.
Do you have any recommendations here? I was thinking one potential solution would be to have the module create a "plugin" function that accepts the schema and adds all the paths, methods, etc. The main app would create an empty schema and add the plugin. Something like:
var somePlugin = require('somemodule').somePlugin;
var SomeSchema = new mongoose.Schema();
SomeSchema.plugin(somePlugin);
mongoose.model('SomeModel', SomeSchema);
Are there any alternatives?
Thanks a lot.
I got around this problem by hacking the prototype of the schema to use the correct instance of mongoose.Schema so that it will pass the internal Mongoose instanceof check. So for your example, I would add a line like this:
var SomeSchema = require('somemodule').SomeSchema;
SomeSchema.__proto__ = mongoose.Schema.prototype;
mongoose.model('SomeModel', SomeSchema);
It's certainly not kosher but it works for me!

nodejs module does not export function

I ran into an issue with my Nodejs application.
I have two different apps that are using shared library, which is located so that it is found one level up in node_modules. So i have this structure ./app1/app.js, ./app2/app.js and ./node_modules/shared.libs/index.js.
shared.libs in its turn has some other modules installed, like mongoose, redis etc. Plus some mogoose models with additional functions in them. All are exported from index.js like this:
exports.async = require('async');
exports.config = require('./config');
exports.utils = require('./lib/utils');
And then in apps i import them like this:
var libs = require('shared.libs');
var config = libs.config;
So after this code i can use config which is coming from that shared library.
This part was and is working just fine. But now i need to put additional layer on top of this library (read: provide more unified interface for both apps).
What i tried to do is to add some functions into index.js of shared library and then export the whole object with these functions. But whenever i try to call previously imported (by var libs = require('shared.libs');) object it says that libs is not defined.
What am i doing wrong here?
I generally want to keep other code the same, so i won't need to go over replacing require part everywhere, but at the same time provide additional functionality from shared library which will be available from that imported libs object.
this should definitely work:
module.exports = {
async: require('async'),
config: require('./config'),
utils: require('./lib/utils'),
foo: function () {
return 'bar';
}
};
reference like:
var libs = require('shared.libs');
console.log(libs.async);
console.log(libs.config);
console.log(libs.utils);
console.log(libs.foo);
console.log(libs.foo());
What makes me wonder is one of your comments above, that you get an error libs is not defined. That looks like you should have other unnoticed errors before.. during the the initialization of your shared.libs module..

Resources