Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Schemas in external module not working in Node.js

I'm having a massive headache try to share some common schema definitions via a module to all the other modules in my code base.

I have a myproj_schemas module that contains these two schemas:

var mongoose = require('mongoose'),
    util = require("util"),
    Schema = mongoose.Schema;

var BaseProfileSchema = function() {
    Schema.apply(this, arguments);

    this.add({
        _user: {type: Schema.Types.ObjectId, ref: 'User', required: true},
        name: {type: String, required: true},
        bio: {type: String, required: true},
        pictureLink: String
    });

};
util.inherits(BaseProfileSchema, Schema);

module.exports = BaseProfileSchema;

And

var mongoose = require('mongoose'),
    BaseProfileSchema = require('./base_profile_schema.js'),
    Schema = mongoose.Schema;

var entSchemaAdditions = {
    mentors: {type: Schema.Types.ObjectId, ref: 'Mentor'}
};


var entrepreneurSchema = new BaseProfileSchema(entSchemaAdditions);

module.exports = entrepreneurSchema;

Mentors is also defined in another file.

My unit tests for these both work in the schemas module.

When I npm install this module and try to create using

Entrepreneur = db.model('Entrepreneur', entrepreneurSchema),

I get the following error:

TypeError: Undefined type at paths.mentors Did you try nesting Schemas? You can only nest using refs or arrays.

If I use the same code in my local module, then no problem. If I reference the schema file directly in the require (e.g. require('../node_modules/myproj_schemas/models/ent_schema') then I get the error.

I'm fairly sure it wasn't breaking like this earlier, but I've backed out all the changes and it is still not working.

I'm drawing a complete blank, and any suggestions would be gratefully received.

EDIT:

I've created a new Schemas module. It has one Schema:

var mongoose = require('mongoose');

var userSchema = new mongoose.Schema({
    email: String
});

module.exports = userSchema;

This also fails when packaged up in a module and npm install'd to other modules.

Running on OS X

like image 846
JonRed Avatar asked Nov 09 '14 05:11

JonRed


People also ask

How NodeJS modules are available externally module?

js Package Manager (npm) is the default and most popular package manager in Node. js ecosystem that is primarily used to install and maintain external modules in Node. js application. Users can basically install the node modules needed for their application using npm.

Does the node wrap all the modules?

It implies the file name of the current module. It refers to the directory name of the current module. Note that every file in Node. js will wrap every file that it executes.

Can not find module in NodeJS?

If you are getting the "Cannot find module" error when trying to run a local file, make sure that the path you passed to the node command points to a file that exists. For example, if you run node src/index. js , make sure that the path src/index. js points to an existing file.


2 Answers

Personally I created separate "common" project with an init method to register all of the models with mongodb, and call the init method in the app.js file of any apps that need access to the models.

  1. Create a shared project - Create a new node project, following the standard process.
  2. package.json - In the shared project, set your package.json file to contain the following entry:

    "main": "index.js"
    
  3. Add a model - Create a new models folder within your shared project, to contain all of your mongoose schemas and plugins. Add your userSchema file to the models folder and name it user.js.

    var mongoose = require('mongoose');
    
    var userSchema = new mongoose.Schema({
        email: String
    });
    
    module.exports = mongoose.model('User', userSchema);
    
  4. index.js - Then in the project's root index.js file create a shared object that can be used by your apps, exposing the models and an init method. There's many ways to code this, but here's how I'm doing it:

    function Common() {
        //empty array to hold mongoose Schemas
        this.models = {};
    }
    
    Common.prototype.init = function(mongoose) {
        mongoose.connect('your mongodb connection string goes here');
        require('./models/user');
        //add more model references here
    
        //This is just to make referencing the models easier within your apps, so you don't have to use strings. The model name you use here must match the name you used in the schema file
        this.models = {
            user: mongoose.model('User')
        }
    }
    
    var common = new Common();
    
    module.exports = common;
    
  5. Reference your common project - However you want to reference your shared project, add the reference to the shared project in the package.json file within your app and give it a name of common. Personally I used GitHub to store the project and referenced the repository path. Since my repository was private I had to use a key in the path, which is covered on the GitHub support site.
  6. Init the models in your app - In the start script for your app (let's assume it's app.js for this example) add the reference to your common project and call the init method to connect to the mongodb server and register the models.

    //at the top, near your other module dependencies
    var mongoose = require('mongoose')
      , common = require('common');
    
    common.init(mongoose);
    
  7. Use the model anywhere in your app - Now that mongoose has the connection pool established and the models have been registered, you can use the models is any of the classes within your app. For example, say you have a page that displays information about a user you could do it like this (untested code, just wrote this for an example):

    var common = require('common');
    
    app.get('/user-profile/:id', function(req, res) {
        common.models.user.findById(req.params.id, function(err, user) {
             if (err)
                 console.log(err.message); //do something else to handle the error
             else
                 res.render('user-profile', {model: {user: user}});
        });
    });
    

EDIT Sorry, I didn't see the line where you were inheriting one schema from another. As one of the other answers stated, mongoose already offers the concept of a plugin. In your example above, you would do this:

In your common module, under '/models/base-profile-plugin.js'

module.exports = exports = function baseProfilePlugin(schema, options){

    //These paths will be added to any schema that uses this plugin
    schema.add({
        _user: {type: Schema.Types.ObjectId, ref: 'User', required: true},
        name: {type: String, required: true},
        bio: {type: String, required: true},
        pictureLink: String
    });

    //you can also add static or instance methods or shared getter/setter handling logic here. See the plugin documentation on the mongoose website.
}

In your common module, under '/models/entrepreneur.js

var mongoose    = require('mongoose')
  , basePlugin  = require('./base-profile-plugin.js');

var entrepreneurSchema   = new mongoose.Schema({
    mentors: {type: Schema.Types.ObjectId, ref: 'Mentor'}
});

entrepreneurSchema.plugin(basePlugin);

module.exports = mongoose.model('Entrepreneur', entrepreneurSchema);
like image 93
Brian Shamblen Avatar answered Oct 12 '22 05:10

Brian Shamblen


Why don't you create a mongoose plugin for common schema like this https://github.com/Keeguon/mongoose-behaviors/blob/master/lib/timestampable.js

like image 1
Dheeraj Kumar Aggarwal Avatar answered Oct 12 '22 06:10

Dheeraj Kumar Aggarwal