Make all fields required in Mongoose - node.js

Mongoose seems to default to make all fields not required. Is there any way to make all the fields required without changing each of:
Dimension = mongoose.Schema(
name: String
value: String
)
to
Dimension = mongoose.Schema(
name:
type: String
required: true
value:
type: String
required: true
)
It'll get really ugly since I have a lot of these.

You could do something like:
var schema = {
name: { type: String},
value: { type: String}
};
var requiredAttrs = ['name', 'value'];
for (attr in requiredAttrs) { schema[attr].required = true; }
var Dimension = mongoose.schema(schema);
or for all attrs (using underscore, which is awesome):
var schema = {
name: { type: String},
value: { type: String}
};
_.each(_.keys(schema), function (attr) { schema[attr].required = true; });
var Dimension = mongoose.schema(schema);

I ended up doing this:
r_string =
type: String
required: true
r_number =
type: Number
required: true
and on for the other data types.

All fields properties are in schema.paths[attribute] or schema.path(attribute);
One proper way to go : define when a field is NOT required,
Schema = mongoose.Schema;
var Myschema = new Schema({
name : { type:String },
type : { type:String, required:false }
})
and make them all required by default :
function AllFieldsRequiredByDefautlt(schema) {
for (var i in schema.paths) {
var attribute = schema.paths[i]
if (attribute.isRequired == undefined) {
attribute.required(true);
}
}
}
AllFieldsRequiredByDefautlt(Myschema)
The underscore way :
_=require('underscore')
_.each(_.keys(schema.paths), function (attr) {
if (schema.path(attr).isRequired == undefined) {
schema.path(attr).required(true);
}
})
Test it :
MyTable = mongoose.model('Myschema', Myschema);
t = new MyTable()
t.save()

Well you could write a mongoose schema plugin function that walked the schema object and adjusted it to make each field required. Then you'd just need 1 line per schema: Dimension.plugin(allRequired).

Mongoose didn't provide the method of setting all fields, but you could do it recursively.
Like Peter mentioned, you could pluginize it in order to reuse the code.
Recursively setting:
// game.model.js
var fields = require('./fields');
var Game = new Schema({ ... });
for(var p in Game.paths){
Game.path(p).required(true);
}
Pluginized:
// fields.js
module.exports = function (schema, options) {
if (options && options.required) {
for(var p in schema.paths){
schema.path(p).required(true);
}
}
}
// game.model.js
var fields = require('./fields');
var Game = new Schema({ ... });
Game.plugin(fields, { required: true });

I'm not sure if there's an easier way to do it in Mongoose, but I would do the following in your IDE/editor:
List out your fields as you would normally:
Dimension = mongoose.Schema(
name: String
value: String
)
Then do a find and replace on String and replace it with {type: String, required: true}, Giving you:
Dimension = mongoose.Schema(
name: {type: String, required: true},
value: {type: String, required: true},
)
Then do the same for Number and other types.

Building on the previous answers, the module below will make fields required by default. The previous answers did not recurse nested objects/arrays.
Usage:
const rSchema = require("rschema");
var mySchema = new rSchema({
request:{
key:String,
value:String
},
responses:[{
key:String,
value:String
}]
});
Node module:
const Schema = require("mongoose").Schema;
//Extends Mongoose Schema to require all fields by default
module.exports = function(data){
//Recursive
var makeRequired = function(schema){
for (var i in schema.paths) {
var attribute = schema.paths[i];
if (attribute.isRequired == undefined) {
attribute.required(true);
}
if (attribute.schema){
makeRequired(attribute.schema);
}
}
};
var schema = new Schema(data);
makeRequired(schema);
return schema;
};

Related

Returning a field which is not in the model

I have projects which store batches of records. When retrieving a project from my mongoDB using mongoose, I want to calculate the number of batches which belong to that specific project.
My Project model-schema currently looks like so:
const schema = new Schema({
name: {
type: String,
required: true,
unique: true
},
slug: {
type: String,
required: true,
unique: true
}
}, {
timestamps: true
})
My batch model-schema looks:
const schema = new Schema({
project:{
type: ObjectId,
ref: 'Project',
required: true
},
file: {
type: String,
required: true
},
orginal_name: {
type: String,
required: true
}
}, {
timestamps: true
})
I have a function which counts the number of batches that belong to a project based off of the batch.project field and then adds to it to the JSON object of the project (e.g project.batchCount). However I have run into a problem where the new field project.batchCount is being removed by the by the models toJSON or toObject function because the field is not present in the model-schema.
My current solution is to add it to the model-schema as a 'dummy field' which is never saved to the mongoDB:
const schema = new Schema({
name: {
type: String,
required: true,
unique: true
},
slug: {
type: String,
required: true,
unique: true
},
batchCount: {
type: Number
}
}, {
timestamps: true
})
However I do not like this way as it makes my model larger than it needs to be and slightly less 'readable'.
Is there a better way to do this? And if so how?
I think what you're looking for is a virtual on the schema. A virtual on a document in mongoose is available locally, but doesn't get saved to the DB.
I liked the idea of using a static on the schema to get the project counts. I also liked the idea of just calling the method on the document rather than the Model, so I implemented it in an instance method. A static would have worked just as well. It depends on your preference.
Here is what I came up with:
batch.js
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const batchSchema = new Schema({
project: {
type: Schema.Types.ObjectId,
ref: 'Project',
required: true
},
file: {
type: String,
required: true
},
original_name: {
type: String,
required: true
}
}, {
timestamps: true
});
batchSchema.virtual('projectCount')
.get(function () {
return this._projectCount;
})
.set(function (v) {
this._projectCount = v;
});
batchSchema.methods.count = async function () {
let found = await this.model('Batch').count({ project: this.project });
this._projectCount = found;
};
const Batch = mongoose.model('Batch', batchSchema);
module.exports = Batch;
The virtual called projectCount has a simple setter and getter to overwrite the value if you need to or retrieve it once it's been set.
The instance method on each document is called count() and calls the Model.count() method with a query for the current document's project _id.
project.js
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const projectSchema = new Schema({
name: {
type: String,
required: true,
unique: true
},
slug: {
type: String,
required: true,
unique: true
}
}, {
timestamps: true
});
const Project = mongoose.model('Project', projectSchema);
module.exports = Project;
populate.js
#!/usr/bin/env node
'use strict';
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
const conn = mongoose.connection;
const Batch = require('./batch');
const Project = require('./project');
const projects = [];
const batches = [];
for (let i = 0; i < 10; i++) {
const project = new Project({
name: `project${i}`,
slug: `slug${i}`
});
for (let j = 0; j < 10; j++) {
const batch = new Batch({
project: project._id,
file: `file${j}`,
original_name: `name${j}`
});
batches.push(batch);
}
projects.push(project);
}
async function add () {
await conn.dropDatabase();
const savedProjects = await Project.create(projects);
const savedBatches = await Batch.create(batches);
console.log(`Added ${savedProjects.length} projects.`);
console.log(`Added ${savedBatches.length} batches.`);
return conn.close();
}
add();
populate.js is just how I created the collections and the docs for this example, nothing fancy here.
get.js
#!/usr/bin/env node
'use strict';
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
const conn = mongoose.connection;
const Batch = require('./batch');
async function get () {
let f = await Batch.findOne({});
await f.count();
console.log(`the project for this batch has ${f.projectCount} matches`);
return conn.close();
}
get().catch(console.error);
Once the instance method count() is called, we have access to the value stored in the virtual projectCount.
bash output
49729301: ./populate.js
Added 10 projects.
Added 100 batches.
49729301: ./get.js
the project for this batch has 10 matches
49729301:
mongo shell output
49729301: mongo --quiet test
> db.batches.findOne()
{
"_id" : ObjectId("5acbbebb4cd320cb4e403e8f"),
"project" : ObjectId("5acbbebb4cd320cb4e403e8e"),
"file" : "file0",
"original_name" : "name0",
"createdAt" : ISODate("2018-04-09T19:27:55.395Z"),
"updatedAt" : ISODate("2018-04-09T19:27:55.395Z"),
"__v" : 0
}
>
as you can see, virtual properties are only available locally, and do not get stored in the db.

TypeError object is not a function

I'm developing an application in node.js with MongoDB
I have to files:
product.js and displaycost.js
this is product .js:
var mongoose = require('mongoose');
var Category = require('./category');
var productSchema = {
name:{type: String, require: true},
//Pictures mus start with "http://"
pictures:[{type:String, match: /^http:\/\//i}],
price:{
amount:{type: Number, required: true},
//ONly 3 supported currencies for now
currency:{
type: String,
enum:['USD','EUR','GBP'],
required: true
}
},
category: Category.categorySchema
};
var schema = new mongoose.Schema(productSchema);
var currencySymbols ={
'USD': '$',
'EUR':'€',
'GBP':'£'
};
/*
*
*/
schema.virtual('displayPrice').get(function(){
return currencySymbols[this.price.currency] +
'' + this.price.amount;
});
schema.set('toObject', {virtuals:true});
schema.set('toJSON', {virtuals:true});
module.exports = schema;
What I need is create a record with "productSchema"
I tried with this:
var Product = require('./product.js');
var p = new Product({
name: 'test',
price:{
amount : 5,
currency: 'USD'
},
category: {
name: 'test'
}
});
console.log(p.displayPrice); // "$5"
p.price.amount = 20;
console.log(p.displayPrice); //" $20"
//{... "displayPrice: "$20",...}
console.log(JSON.stringify(p));
var obj = p.toObject();
But when I run the displaycost.js throw me an error at the word "new"
and writes "TypeError: object is not a function"
I don't know why is happening that. Thank you.
you missing export mongoose model with model name.
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
Category = require('./category');
var productSchema = new Schema({
name: {type: String, require: true},
pictures: [{type: String, match: /^http:\/\//i}],
price: {
amount: {type: Number, required: true},
currency: {
type: String,
enum: ['USD', 'EUR', 'GBP'],
required: true
}
},
category: Category
});
var currencySymbols = {
'USD': '$',
'EUR': '€',
'GBP': '£'
};
productSchema.virtual('displayPrice').get(function () {
return currencySymbols[this.price.currency] +
'' + this.price.amount;
});
productSchema.set('toObject', {virtuals: true});
productSchema.set('toJSON', {virtuals: true});
module.exports = mongoose.model('Product', productSchema);
"new" can only be called on a function. not an object.
In program.js you are creating a new instance of the mongoose schema.
var schema = new mongoose.Schema(productSchema);
... more code ...
return schema;
In your other js file you require product. At this point in time product.js has returned an object to you. A new mongoose schema. It is an object which you are refering to as Product.
You then try to create a new instance of it by calling new on the product OBJECT. New cannot be called on an object literal. It can only be called on a function.
I don't believe you need to make a new instance of schema within product.js, just return the function that creates the schema. Then call new on it when you require it like you are in your second file.

NodeJS Mongo - Mongoose - Dynamic collection name

So, I want to create a client side based paritioning schema, where I set the collection name as function(), my pseudo code is something like that:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
var ConvForUserSchema = new Schema({
user_id: Number,
conv_hash: String,
archived: Boolean,
unread: Boolean
}, function CollectionName() {
return (this.user_id % 10000);
});
Is this in any way possible through moongose such that both read and writes will work as expected?
Hello you just need to declare schema model with your dinamically name, like this:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// our schema
function dynamicSchema(prefix){
var addressSchema = new Schema({
dir : {type : String, required : true}, //los 2 nombres delimitados por coma (,) ej. Alberto,Andres
city : {type : String, required: true}, //la misma estructura que para los nombres ej. Acosta, Arteta
postal : {type : Number, required : true},
_home_type : {type : Schema.Types.ObjectId, required : true, ref : prefix + '.home_type'},
state : {type : String, required : true},
telefono : String,
registered : {type : Date, default: Date.now }
});
return mongoose.model(prefix + '.address', addressSchema);
}
//no we export dynamicSchema function
module.exports = dynamicSchema;
so in your code anywhere you can do this:
var userAdress = require('address.js')(id_user);
var usrAdrs1 = new userAddress({...});
userAdrs1.save();
Now go to your mongo shell & list collections (use mydb then show collections), you will see a new collection for address with uid prefix. In this way mongoose will create a new one collection address for each different user uid.
Use the function to get the model dynamically.
/*
* Define Schemas as you used to
*/
const ConvForUserSchema = new Schema({
user_id: Number,
conv_hash: String,
archived: Boolean,
unread: Boolean
},{
versionKey : false,
strict: false
});
/*
* Define the dynamic function
*/
const models = {};
const getModel = (collectionName) => {
if( !(collectionName in models) ){
models[collectionName] = connection.model(
collectionName, ConvForUserSchema, collectionName
);
}
return models[collectionName];
};
Then get the dynamic model using the function
const result = getModel("YourCollectionName").findOne({})
Collection name logic is hard coded all over the Moongose codebase such that client side partitioning is just not possible as things stands now.
My solution was to work directly with the mongo driver -
https://github.com/mongodb/node-mongodb-native
This proved great, the flexibility working with the driver directly allows for everything required and the Moongose overhead does not seem to add much in any case.
Implemented:
//Require Mongoose
const mongoose = require('mongoose');
const moment = require('moment');
//Define a schema
const Schema = mongoose.Schema;
const EntranceModelSchema = new Schema({
name: String,
birthday: Date,
gender: String,
phoneNumber: {type: String, require: true},
email: String,
address: String,
addressReference: String,
addressLatitude: {type: Number, require: true},
addressLongitude: {type: Number, require: true},
vehicleReference: String,
date: Date
});
//Export function to create "SomeModel" model class
module.exports = function(){
let dateSuffix = moment().format('MMMDoYYYY');
const collectionName = `Entrance${dateSuffix}`;
return mongoose.model(collectionName, EntranceModelSchema);
};
Gomosoft's solution works. It needs some amends but the idea works nicely.
The above solution works only the first time. If you are trying to send a second request to the same collection, it will throw an error for trying to overwrite the model that is already defined. So I had to tweak it as follows:
var Rating = require('./app/models/rating');
var myRating;
router.route('/ratings/:user_id')
.post(function(req,res){
var user_id = req.params.user_id;
if(myRating == undefined){
myRating = Rating(user_id);
}
...
rating.save(...);
});
Because I'm checking if myRating is undefined, it will create this reference only once. So no errors will occur.
Hope this helps.
I am adding to the answer by Javier Gomez, to give a solution to Exis Zang's "OverwriteModelError: Cannot overwrite xxx model once compiled" problem. The Schema model file can store an array of the dynamic models based on the Singleton pattern. If that model already exists return it, otherwise create it with new, store it and return it:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const Addresses = {}
// our schema
function DynamicSchema(prefix){
var addressSchema = new Schema({
dir : {type : String, required : true}, //los 2 nombres delimitados por coma (,) ej. Alberto,Andres
city : {type : String, required: true}, //la misma estructura que para los nombres ej. Acosta, Arteta
postal : {type : Number, required : true},
_home_type : {type : Schema.Types.ObjectId, required : true, ref : prefix + '.home_type'},
state : {type : String, required : true},
telefono : String,
registered : {type : Date, default: Date.now }
});
return mongoose.model(prefix + '.address', addressSchema);
}
// this function will store the model in the Addresses object
// on subsequent calls, if it exists, it will return it from the array
function getAddressModel(prefix) {
if (!Addresses[prefix]) {
Addresses[prefix] = new DynamicSchema(prefix)
}
return Addresses[prefix]
}
//now we export getAddressModel function
module.exports = getAddressModel;
To Create a dynamic collection follow the below steps,
const mongoose = require('mongoose');
function createCompanyDynamicSchema(prefix) {
let collectionName = prefix + '_company';
companySchema = new mongoose.Schema(
{
name: { type: String },
enabled: { type: Number, default: 1 },
},
{ timestamps: true },
{ versionKey: false },
{ strict: false }
);
collectionName = mongoose.model(collectionName, companySchema);
return collectionName;
}
module.exports = { createCompanyDynamicSchema };
To call this method from any file,
let companySchema = require('./schema');
_this.createCompany = function () {
return new Promise(async (resolve, reject) => {
let companyCollection = companySchema.createCompanyDynamicSchema('IO');
let addr = new companyCollection({ first_name: 'test' });
addr.save();
});
};
To query from dynamically created collection,
_this.getCompany = function () {
return new Promise(async (resolve, reject) => {
let companyCollection = companySchema.createCompanyDynamicSchema('IO');
let data = await companyCollection.model('IO_users').find();
console.log(data);
});
};

How to create interdependent schemas in Mongoose?

I have two Schemas and I want them to interact with eachother. For instance:
// calendar.js
var mongoose = require('mongoose');
var Scema = mongoose.Schema;
var Day = mongoose.model('Day');
var CalendarSchema = new Schema({
name: { type: String, required: true },
startYear: { type: Number, required: true }
});
CalendarSchema.methods.getDays = function(cb){
Day.find({ cal: this._id }, cb);
}
module.exports = mongoose.model('Calendar', CalendarSchema);
// day.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var Calendar = mongoose.model('Calendar');
var DaySchema = new Schema({
cal: { type: ObjectId, required: true },
date: { type: Number, required: true },
text: { type: String, default: 'hello' }
});
DaySchema.methods.getCal = function(cb){
Calendar.findById(this.cal, cb);
}
module.exports = mongoose.model('Day', DaySchema);
However, I get an error because each schema depends on the other one. Is there a way to get this working using Mongoose? I include them like this:
// app.js
require('./models/calendar');
require('./models/day');
I realize this is an ancient thread, but I'm sure posting the solution will help others down the road.
The solution is to export the module before requiring the interdependent schemas:
// calendar.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CalendarSchema = new Schema({
name: { type: String, required: true },
startYear: { type: Number, required: true }
});
module.exports = mongoose.model('Calendar', CalendarSchema);
// now you can include the other model and finish definining calendar
var Day = mongoose.require('./day');
CalendarSchema.methods.getDays = function(cb){
Day.find({ cal: this._id }, cb);
}
// day.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var DaySchema = new Schema({
cal: { type: ObjectId, required: true },
date: { type: Number, required: true },
text: { type: String, default: 'hello' }
});
module.exports = mongoose.model('Day', DaySchema);
// same thing here. require after exporting
var Calendar = require('./calendar');
DaySchema.methods.getCal = function(cb){
Calendar.findById(this.cal, cb);
}
It really is that simple. Explanation by Brian Bickerton can be found here:
http://tauzero.roughdraft.io/3948969265a2a427cf83-requiring-interdependent-node-js-modules
It's nice to be able to use functions by name within a module instead of the lengthy module.exports.name. It's also nice to have a single place to look and see everything to be exported. Typically, the solution I've seen is to define functions and variables normally and then set module.exports to an object containing the desired properties at the end. This works in most cases. Where it breaks down is when two modules are inter-dependent and require each other. Setting the exports at the end leads to unexpected results. To get around this problem, simply assign module.exports at the top, before requiring the other module.
You need require the files. If they are in the same path do this:
//calendar.js
var Day = require('./day');
/* Other logic here */
var CalendarSchema = new Schema({
name: { type: String, required: true },
startYear: { type: Number, required: true }
})
, Calendar;
/* other logic here */
/* Export calendar Schema */
mongoose.model('Calendar', CalendarSchema);
Calendar = mongoose.model('Calendar');
exports = Calendar;
Do the same in day.js
EDIT: As JohnnyHK says this don`t work. Link to explanation

File Structure of Mongoose & NodeJS Project

I currently have all my models (Schema definitions) in the /models/models.js file for my Mongoose/NodeJS application.
I'd like to break these apart into different files as such: user_account.js, profile.js, etc. However I cannot seem to do so as my controllers break and report back "cannot find module" once I pull these classes apart.
My project structure is as follows:
/MyProject
/controllers
user.js
foo.js
bar.js
// ... etc, etc
/models
models.js
server.js
The contents of my models.js file looks like this:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
mongoose.connect('mongodb://localhost/mydb');
var UserAccount = new Schema({
user_name : { type: String, required: true, lowercase: true, trim: true, index: { unique: true } },
password : { type: String, required: true },
date_created : { type: Date, required: true, default: Date.now }
});
var Product = new Schema({
upc : { type: String, required: true, index: { unique: true } },
description : { type: String, trim: true },
size_weight : { type: String, trim: true }
});
My user.js file (controller) looks like this:
var mongoose = require('mongoose'),
UserAccount = mongoose.model('user_account', UserAccount);
exports.create = function(req, res, next) {
var username = req.body.username;
var password = req.body.password;
// Do epic sh...what?! :)
}
How can I break the schema definition into multiple files and also reference it from my controller? When I do reference it (after the schema is in a new file) I get this error:
*Error: Schema hasn't been registered for model "user_account".*
Thoughts?
Here's a sample app/models/item.js
var mongoose = require("mongoose");
var ItemSchema = new mongoose.Schema({
name: {
type: String,
index: true
},
equipped: Boolean,
owner_id: {
type: mongoose.Schema.Types.ObjectId,
index: true
},
room_id: {
type: mongoose.Schema.Types.ObjectId,
index: true
}
});
var Item = mongoose.model('Item', ItemSchema);
module.exports = {
Item: Item
}
To load this from an item controller in app/controllers/items.js I would do
var Item = require("../models/item").Item;
//Now you can do Item.find, Item.update, etc
In other words, define both the schema and the model in your model module and then export just the model. Load your model modules into your controller modules using relative require paths.
To make the connection, handle that early in your server startup code (server.js or whatever). Usually you'll want to read the connection parameters either from a configuration file or from environment variables and default to development mode localhost if no configuration is provided.
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost');
A couple answers here really helped me develop an alternative approach. The original question is regarding breaking just the Schema definition out, but I prefer to bundle the Schema and Model definitions in the same file.
This is mostly Peter's idea, only exporting the model definition by overriding module.exports to make accessing the model from your controller a little less verbose:
Project layout:
MyProject
/controllers
user.js
foo.js
bar.js
// ... etc, etc
/models
Item.js
server.js
models/Item.js would look like:
var mongoose = require("mongoose");
var ItemSchema = new mongoose.Schema({
name: {
type: String,
index: true
}
});
module.exports = mongoose.model('Item', ItemSchema);
// Now `require('Item.js')` will return a mongoose Model,
// without needing to do require('Item.js').Item
And you access the model in a controller, say user.js, like:
var Item = require(__dirname+'/../models/Item')
...
var item = new Item({name:'Foobar'});
Don't forget to call mongoose.connect(..) in server.js, or wherever else you deem appropriate!
I recently answered a Quora question with regard to this same problem.
http://qr.ae/RoCld1
What I have found very nice and saves on the amount of require calls is to structure your models into a single directory. Make sure you only have a single model per file.
Create an index.js file in the same directory as your models. Add this code to it. Be sure to add the necessary fs require
var fs = require('fs');
/*
* initializes all models and sources them as .model-name
*/
fs.readdirSync(__dirname).forEach(function(file) {
if (file !== 'index.js') {
var moduleName = file.split('.')[0];
exports[moduleName] = require('./' + moduleName);
}
});
Now you can call all your models as follows:
var models = require('./path/to/models');
var User = models.user;
var OtherModel = models['other-model'];
Peter Lyons pretty much covered the basis.
Borrowing from the above example (removing the lines after the schema) I just wanted to add:
app/models/item.js
note: notice where `module.exports` is placed
var mongoose = require("mongoose");
var ItemSchema = module.exports = new mongoose.Schema({
name: {
type: String,
index: true
},
...
});
To load it from the app/controllers/items.js
var mongoose = require('mongoose');
var Item = mongoose.model('Item', require('../models/item'));
Another way without the module.exports or require:
app/models/item.js
var mongoose = require("mongoose");
var ItemSchema = new mongoose.Schema({
name: {
type: String,
index: true
},
...
});
mongoose.model('Item', ItemSchema); // register model
In the app/controllers/items.js
var mongoose = require('mongoose')
, Item = mongoose.model('Item'); // registered model
Inspired by sequelize-cli, I have a models directory where i define all schema.
Complete app on github: https://github.com/varunon9/node-starter-app-mongo
models/index.js-
'use strict';
const fs = require('fs');
const path = require('path');
const mongoose = require('mongoose');//.set('debug', true);
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
const Schema = mongoose.Schema;
fs
.readdirSync(__dirname)
.filter(fileName => {
return (
fileName.indexOf('.') !== 0)
&& (fileName !== basename)
&& (fileName.slice(-3) === '.js'
);
})
.forEach(fileName => {
const model = require(path.join(__dirname, fileName));
const modelSchema = new Schema(model.schema);
modelSchema.methods = model.methods;
modelSchema.statics = model.statics;
// user.js will be user now
fileName = fileName.split('.')[0];
db[fileName] = mongoose.model(fileName, modelSchema);
});
module.exports = db;
models/user.js-
'use strict';
module.exports = {
schema: {
email: {
type: String,
required: true,
unique: true,
},
mobile: {
type: String,
required: false
},
name: {
type: String,
required: false
},
gender: {
type: String,
required: false,
default: 'male'
},
password: {
type: String,
required: true
},
dob: {
type: Date,
required: false
},
deactivated: {
type: Boolean,
required: false,
default: false
},
type: {
type: String,
required: false
}
},
// instance methods goes here
methods: {
},
// statics methods goes here
statics: {
}
};
I like using classes to organize everything, maybe try this:
const mongoose = require('mongoose')
class UserAccount {
constructor() {
this.schema = new mongoose.Schema({
user_name: { type: String, required: true, lowercase: true, trim: true, index: { unique: true } },
password: { type: String, required: true },
date_created: { type: Date, required: true, default: Date.now }
});
this.model = new mongoose.model('UserAccount', this.schema)
}
create = (obj) => {
return new Promise((resolve, reject) => {
this.model.create({ ...item })
.then((result) => {
resolve(result)
}).catch((err) => {
reject(err)
});
});
}
}
module.exports = UserAccount;
This approach allows you to add custom methods. Plus it combines the controller/model into a single vector, allowing you to detach the model at any time. This may not scale in enterprise, but it may suite smaller apps.

Resources