OOP with MVC using Mongoose & Express.js - node.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.

Related

Creating a mongoose Schema using a javascript object

This might seem like a really silly question, but is it possible to pass a js object instead of having to write the key value pairs during a Schema assignment?
So, instead of let schema = new mongoose.Schema({name: String}), can you say
const mongoose = require('mongoose')
let obj = {name: String}
let schema = new mongoose.Schema(obj)
Thanks!
Yes, you can pass the key: type pairs, validations etc in them as an object in the Schema. Basically, what it does, it passes the definition provided in the object to the schema constructor.
constructor(definition?: SchemaDefinition, options?: SchemaOptions);
But whenever you write as an object way, autocomplete and other types won't be available (IDE intellisense) and causes more error-prone code when you compile it and also there is no effect on the database by doing this way.
The answer is: You can't.
Because that's how Mongoose works. That's a framework with many rules that we have to obey in order to successfully leverage its functionalities.

Google Cloud Datastore ORM

Is there some high level ORM for nodejs datastore client library? Because it becoming really hard to maintain relatively small application when entities referencing and objects versioning takes place. Just interested, should I start write my own bicycle or someone already wrote something like that?
If not, maybe there some separate libraries to implemented appropriate referencing mechanism?
Check out gstore-node:
gstore-node is a Google Datastore entities modeling library for Node.js inspired by Mongoose and built on top of the #google-cloud/datastore library.
It is not a replacement of #google-cloud/datastore but a tool built to help modeling Entities through Schemas and to help validating the data saved in the Datastore.
You may try on this one, very well structure, written in Typescript.
https://www.npmjs.com/package/ts-datastore-orm
import {BaseEntity, Column, Entity} from "ts-datastore-orm";
#Entity({kind: "user"})
export class User extends BaseEntity {
#Column({generateId: true})
public id: number = 0;
#Column({index: true})
public total: number = 0;
}
async function main() {
const user = User.create();
await user.save();
const id = user.id;
}

Mongoose - add global method to all models

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);

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!

Resources