67

In my models/user.js file:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var userSchema = new Schema({
    (define schema)
});
...
(save user)
...
(check password)
...
mongoose.model('User', userSchema);

And in my router/index.js, I have:

var mongoose = require('mongoose');
var User = mongoose.model('User');

which throws the error:

MissingSchemaError: Schema hasn't been registered for model "User".

If however, in user.js, I do (in the last line)

module.exports = mongoose.model('User', userSchema);

and in index.js I do var User = require('../models/User');, then everything works.

But it should not, because in config/pass.js I am doing var User = mongoose.model('User'); and it's working flawlessly.

The require('../models/User'); syntax isn't working on Ubuntu, but is on my Mac.

What should I do? How do I fix it? I have looked at tons of sample apps, including MEAN but nothing was really helpful.

KGo
  • 18,536
  • 11
  • 31
  • 47
  • I ran into a similar problem, and while the answer from @verybadalloc was helpful, I wanted to see if I could solve it another way. Then, when I undid my changes, it still worked. So, now I am still confused, but your question and the answer helped. I tried changing my exports to also export the schema, and that seemed to help, but now I'm not entirely sure what worked since undoing the changes seems to have no impact. – PrairieProf Aug 18 '14 at 21:06

20 Answers20

77

I got the same problem when I am trying the MEAN tutorial.

After done a little bit research, I found that in app.js, if I put require("./models/User") before var routes = require("./routes/index"), then it works.

Like this:


mongoose.connect("mongodb://localhost/news");
require("./models/Posts");
require("./models/Comments");

var routes = require('./routes/index');
var users = require('./routes/users');

var app = express();

Hope the answer will be helpful!

Ahei Cheng
  • 771
  • 5
  • 5
43

The error occurs because the models/user.js has not been interpreted by the time router/index.js has been loaded. One way to solve this would be to do the following:

var mongoose = require('mongoose');
//Load all your models
var User = require('./../models/user.js');

//Now, this call won't fail because User has been added as a schema.
mongoose.model('User');

This, however, turns out to be against best practises, which dictates that all this config stuff should happen at the start of your app.js file. Look at this example from madhums' example project

var models_path = __dirname + '/app/models'
fs.readdirSync(models_path).forEach(function (file) {
  if (~file.indexOf('.js')) require(models_path + '/' + file)
})

Note that he is loading his models before setting the app's router. As for the Ubuntu vs Mac issue, I believe it is because a relative path in Ubuntu has to start with ./. You just have to change it to ./../models/user.js, which works on Mac.

Amol M Kulkarni
  • 21,143
  • 34
  • 120
  • 164
verybadalloc
  • 5,768
  • 2
  • 33
  • 49
  • Where should that code go? And to my surprise, removing `./` worked in both systems, but `./` didn't work on Ubuntu. – KGo Dec 30 '13 at 02:53
  • In the file that you config/start your express server - before your routes file is loaded. – JoeTidee Mar 18 '18 at 21:08
13

I literally research lot and I found a solution so, I share this solution to you so, no one can face that cheap mistake that I did.

Please remember, you just need to add the same name in ref as you gave in your model because its case sensitive ( Product !== product ).

const Product = new mongoose.model('Product', productSchema);

product: {
    type: mongoose.Schema.ObjectId,
    ref: 'Product', <-- Should be same as modelName
    required: [true, 'Cart must belong to a product']
}
Nisharg Shah
  • 16,638
  • 10
  • 62
  • 73
10

All code in your mongoose schema js files should have run before it is used in other files.

For example, the following code snippet makes sure the mongoose schema files/modules are executed.

fs.readdirSync(__dirname + '/app/models').forEach(function (file) { if (~file.indexOf('.js')) require(__dirname + '/app/models/' + file); });

or schema files can be manually executed by calling

var User = require('./app/models/user.js')

before the models are used anywhere in the application.

Once the above is done, other modules that uses mongoose models can be required/executed.

9

I've also experienced this error with ES6/Typescript. Even I imported the model, the error still persisted. According to docs here

MongooseError.MissingSchemaError

Thrown when you try to access a model that has not been registered yet

    import { model, Schema } from 'mongoose';
    import Company from './CompanyModel';

    const ProjectSchema = new Schema({
        company: { type: Schema.Types.ObjectId, ref: "Company" }
    });

    export default model('Project', ProjectSchema);

The tips was just to make sure to use the model explicitly, so changing ref:"Company" into ref:Company.modelName seemed fixed it.

I hope that helps some of you

irzhy
  • 920
  • 1
  • 9
  • 15
6

This problem occurred when try to get model before load the model file load

I solved same problem in my mean.io project

In controller:

'use strict';
require('../models/settingsModel'); // load settingsModel.js file before get mongoose.model('Settings')
var mongoose = require('mongoose'),
    Settings = mongoose.model('Settings'),
    Q = require('q');
Shaishab Roy
  • 16,335
  • 7
  • 50
  • 68
6

In my case, it happened because of capital/small letter confusion. User model had this:

const userSchema = new Schema({
// ...
});
module.exports = mongoose.model('User', userSchema);

And Product model had a reference to User model but small case:

const productSchema = new Schema({
// ...
  userId: {
    type: Schema.Types.ObjectId,
    ref: 'user', // This should exactly match the name of User model above!!!
    required: true
  }
});
Atilla Baspinar
  • 925
  • 1
  • 12
  • 17
  • I appreciate this...I was using `gearItem` in my schema association reference...but the model itself was named `GearItem` ... whoops Thanks for this additional answer! – twknab Jan 27 '20 at 03:03
  • Saves me. Although I did this before in other projects but I missed this small part which has consumed 3 hours to figure out. – Nitesh Malviya Oct 28 '20 at 12:16
4
  • You'll need to require your model in your code
  • Mongoose won't recognize that you've defined a model until you've called mongoose.model, and that's only called when you require model

Ex.

In the below example, you will get MissingSchemaError: Schema hasn't been registered for model “Role” if you don't do const Role = require("./role");

const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Role = require("./role");
const userSchema = new Schema(
  {
    role: { type: Schema.Types.ObjectId, ref: "Role", required: false },
    username: { type: String, required: true, trim: true },
    password: { type: String, required: true, trim: true },
    email: { type: String, required: true, trim: true }
  },
  { timestamps: true }
);

module.exports = mongoose.model("User", userSchema);
Vaibhav KB
  • 1,695
  • 19
  • 17
2

In using express, one of the common mistake is by requiring the express before mongoose. This results to "MissingSchemaError: Schema hasn't been registered for model "User"." error.

You can easily fix it by correcting the "require" order (i.e mongoose then express)

var mongoose = require('./config/mongoose'), 
    express = require('./config/express');
Wreeecks
  • 2,186
  • 1
  • 33
  • 53
2

I recently came across a solution similar to the answers provided by @rajkumar-nagarajan and @verybadalloc.

I like this approach better, because it avoids the n+1 iteration, and it does not read all the models from the file system.

In app/models/index.js, require each model file:

require('./model1');
require('./model2');
require('./model3');

Then, in /app.js or /index.js (whichever your project uses), you can now simply do:

require('./app/models');

The part I'm missing (any tips appreciated here) --how to easily register each model by name like:

mongoose.model('Model1')
theUtherSide
  • 3,338
  • 4
  • 36
  • 35
1

I got this error while trying to use mongoose-fixture to seed some default data into a mongo collection. Was baffled for long, following this and similar threads for help, trying to debug. Ultimately the issue turned out to be due to mongoose and mongoose-fixture versions in my case.

If no code changes help, or if you get this error while trying to correctly use mongoose-fixture (which is supposed to register your schemas for you), try this. Delete the node_modules dir of your project, run an npm cache clean, and then an npm install.

If even this doesn't help, try comparing versions of mongoose / mongoose-fixture between the problematic app and one that works, and try changing the version in your package.json, and repeating the above steps. This worked for me.

Avik Ray
  • 99
  • 1
  • 2
1

I was making a different mistake like I have created the Schema but the issue was that I didn't use schema model (for insert, update, read and delete the doc).

i.e. I have created the collection something like that:

const playerSchema = new Schema({
    bowling: {
        style: String,
        arm: String,
    }
})

export const PlayerSchema = model('Player', playerSchema)

but didn't use or call PlayerSchema model somewhere that's why I was getting this error.

WasiF
  • 26,101
  • 16
  • 120
  • 128
1

If you are facing this error because of ref field in your schema, you need to provide exact name that you used for that modeling reference schema (its case sensitive).

// models/User.js
const userSchema = new Schema({ name: String });
module.exports = mongoose.model('User', userSchema); // note the 1st arg


// models/Post.js
const postSchema = new Schema({ 
   user: { 
     type: mongoose.Schema.Types.ObjectId, 
     ref: 'User' // `ref` must be 'User' not 'user'
   },
})

module.exports = mongoose.model('Post', postSchema)

Also while calling .populate() on query, the reference model must be modeled before calling it. This is because models/User.js is not executed anywhere else in here, so by importing you will make User available in mongoose.models object.

In a real project the User model file be imported at least once through out the project, so just make sure you have imported reference model at least once.

// your app
const User = require('./models/User') // important to import
const Post = require('./models/Post')

//console.log(!!mongoose.models.User) // true
//..
const post = await Post.findOne().populate({ path : 'user' })

Alternative way is to import User model in Post model and use it for ref field.

// models/User.js
const userSchema = new Schema({ name: String });
module.exports = mongoose.model('User', userSchema);


// models/Post.js
const User = require('./User')

const postSchema = new Schema({ 
   user: { 
     type: mongoose.Schema.Types.ObjectId, 
     ref: User, // here we are directly referring model instance itself instead of model name.
   }
});

module.exports = mongoose.model('Post', postSchema)

& of course there are many approaches for solving this issue.

bogdanoff
  • 1,705
  • 1
  • 10
  • 20
1

I Got The same problem when working with nextjs and using populate on the find query

I solved it by using:

categories: [{ type: Schema.Types.ObjectId, ref: Category.modelName, required: true }]

Instead of:

categories: [{ type: Schema.Types.ObjectId, ref: 'Category', required: true }] 

Why it solved the problem. because it made sure the category-schema is registered.

Daniel Dan
  • 86
  • 3
0

I encountered this issue when tried to add a new model on the base code of this tutorial User Auth on MEAN stack. The solution is like what Ahei mentioned.

Specifically, I need to add a line require('path/to/your/model') in the end of /app_api/models/db.js which is required in the app.js. It is better to keep the consistency of the structure for the ease of development.

Community
  • 1
  • 1
peteroid
  • 1,116
  • 10
  • 13
0

I tried all the above solutions but all of them failed instead I found that the solution was to clear my database (MongoDB) and then rerunning the app

Bug
  • 243
  • 3
  • 15
0

The problem arises in the require() statements for the models. You need to move all the MongoDB dependencies above the routes dependencies like this eg

//blog.js file

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const blogSchema = new Schema({
title : String
});

mongoose.model('blogs', blogSchema);

in server.js

//server.js file
require('dotenv').config();
const mongoose = require('mongoose');
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
require('./models/blog');
const Blog = mongoose.model('blogs');

const URL = process.env.MONGO_DB_URL;
 mongoose.connect(URL, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

mongoose.connection
.once('open', () => console.log('Connected to MongoLab instance.'))
.on('error', (error) => console.log('Error connecting to MongoLab:', error));

const app = express();

app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.get('/',  (req,res)=>{
res.send('Hello World')
});

 app.post('/blog',async (req, res) =>{
    const blog = await new Blog(req.body).save();
    res.send('just an example');
  });
alphacat
  • 61
  • 1
  • 4
0

In my case I couldn't use populate option without registering UserRole schema.

Role Schema

import mongoose from "mongoose";
const { Schema } = mongoose;

export interface IRole{
  role:string;
}

const userRoleSchema = new Schema({
  role: String,
});

// compile our model
const UserRole = mongoose.model<IRole>("Role", userRoleSchema);
export default UserRole;

User Schema

import mongoose, { Types } from "mongoose";
const { Schema } = mongoose;

export interface IUser extends mongoose.Document {
 firstname:string;
 lastname:string;
 email:string;
 password:string;
 userRole:Types.ObjectId
}

const userSchema = new Schema({
  firstname: String, // String is shorthand for {type: String}
  lastname: String,
  email: String,
  password: String,
  userRole: { type: Schema.Types.ObjectId, ref: "Role", required:true },
  createdDate: { type: Date, default: Date.now },
});

// compile our model
const User = mongoose.model<IUser>("User", userSchema);
export default User;

Register Schemas

import mongoose from "mongoose";
import UserRole from "./role.schema";
import User from "./user.schema";

export const registerSchemas = () => {
    mongoose.model('Role', UserRole.schema);
    mongoose.model('User', User.schema);
} 

I had to register all schemas like this after MongoDB connection initialization.

connectDB()
  .then(() => {
    console.log("Connected to MongoDb");
    registerSchemas()
  })
  .catch((err) => console.log(err));

Then didn't get the "MissingSchemaError" message.

enter image description here

This is the error. (when I didn't register the UserRole model.)

User.findOne({ email: authData.email }, {}, { populate: ["userRole"] })
  .then((user) => {
    console.log("User", user);
    if (user) {
      if (user.password !== authData.password) {
        res.send("invalid Email or Password");
      } else {
        res.send(user);
      }
    } else {
      res.send("User not found!");
    }
  })
  .catch((err) => {
    console.log(err);
    res.send(err);
  });

Populate option didn't work without registerSchemas function.

enter image description here

While this answer may not be directly relevant to your question, I hope you find it helpful :)

Lojith Vinsuka
  • 906
  • 1
  • 10
  • 8
0

The simplest solution according to me is based on the type of JavaScript you are using.

  1. If you are using Common JS, you can create the Schema as follows:
const mongoose=require('mongoose')
const userSchema=new mongoose.Schema({
//Define your Schema here
})
mongoose.model('User',userSchema)

You'll need to require/load the Schema first into a variable and use it in your model thereon, like:

const mongoose = require('mongoose');
const User=mongoose.model('User')
  1. If you are using ECMA Script or ES6 (Where we work with import rather than require). You necessarily need to export the Schema from the model.
import mongoose from 'mongoose'
const userSchema=new mongoose.Schema({
//Define your Schema here
})
export default mongoose.model('User',userSchema)

And you will simply have to import it wherever you are using it without defining the model again as you have already exported the complete model by the name 'User'.

import User from './../models/user.js'

This will solve the problem. However, this will work only in case of export default. So, you'll have to define a seperate Schema file for each Schema. I'll update the answer if I find a way to export multiple Schemas from one file.

-10

If you are using the Mean.js stack, run the test with:

grunt test

And not with

mocha
user253751
  • 57,427
  • 7
  • 48
  • 90
Maçois
  • 9
  • 1
  • 3