Kenny Kenny - 6 months ago 31
Node.js Question

MEAN throw new mongoose.Error.MissingSchemaError(name);

I'm working with the MEAN stack and am quite new to it. I'm nearly done with my application and I'm adding in authentication, so I've created a new schema for users. I'm pretty sure I did EXACTLY everything the book has demonstrated, but the book has had a couple of bugs here and there, despite being published in 2015. Here's my error below and the code below that:

enter image description here

=========================================================================

app.js


require('dotenv').load();
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var uglifyJs = require("uglify-js");
var fs = require('fs');
var passport = require('passport');

require('./app_api/models/db');
require('./app_api/config/passport');

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

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'app_server', 'views'));
app.set('view engine', 'jade');

var appClientFiles = [
'app_client/app.js',
'app_client/home/home.controller.js',
'app_client/about/about.controller.js',
'app_client/locationDetail/locationDetail.controller.js',
'app_client/reviewModal/reviewModal.controller.js',
'app_client/common/services/geolocation.service.js',
'app_client/common/services/loc8rData.service.js',
'app_client/common/filters/formatDistance.filter.js',
'app_client/common/filters/addHtmlLineBreaks.filter.js',
'app_client/common/directives/navigation/navigation.directive.js',
'app_client/common/directives/footerGeneric/footerGeneric.directive.js',
'app_client/common/directives/pageHeader/pageHeader.directive.js',
'app_client/common/directives/ratingStars/ratingStars.directive.js'
];
var uglified = uglifyJs.minify(appClientFiles, { compress : false });

fs.writeFile('public/angular/loc8r.min.js', uglified.code, function(err) {
if(err) {
console.log(err);
} else {
console.log('Script generated and saved: loc8r.min.js');
}
});

// app.locals.pretty = true;

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'app_client')));

app.use(passport.initialize());

// app.use('/', routes);
app.use('/api', routesApi);
app.use('/users', users);

app.use(function(req, res) {
res.sendFile(path.join(__dirname, 'app_client', 'index.html'));
});

// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});


module.exports = app;


=========================================================================

/app_api/models/db.js


var mongoose = require('mongoose');
var gracefulShutdown;
var dbURI = 'mongodb://localhost/Loc8r';

if (process.env.NODE_ENV === 'production') {
dbURI = process.env.MONGOLAB_URI;
}
mongoose.connect(dbURI);

// Emulate SIGINT signal for Windows
var readLine = require('readline');
if (process.platform === "win32") {
var rl = readLine.createInterface ({
input: process.stdin,
output: process.stdout
});
rl.on ('SIGINT', function() {
process.emit ("SIGINT");
});
}

mongoose.connection.on('connected', function() {
console.log('Mongoose connected to ' + dbURI);
});
mongoose.connection.on('error', function(err) {
console.log('mongoose connection error: ' + err);
});
mongoose.connection.on('disconnected', function() {
console.log('Mongoose disconnected');
});

gracefulShutdown = function(msg, callback) {
mongoose.connection.close(function() {
console.log('Mongoose disconnected through ' + msg);
callback();
});
};

// For nodemon restarts
process.once('SIGUSR2', function() {
gracefulShutdown('nodemon restart', function() {
process.kill(process.pid, 'SIGUSR2');
});
});
// For app termination
process.on('SIGINT', function() {
gracefulShutdown('app termination', function() {
process.exit(0);
});
});
// For Heroku app termination
process.on('SIGTERM', function() {
gracefulShutdown('Heroku app shutdown', function() {
process.exit(0);
});
});

// BRING IN SCHEMAS & MODELS
require('./locations');
require('./users');


=========================================================================

app_client/models/users.js


var mongoose = require('mongoose');
var crypto = require('crypto');
var jwt = require('jsonwebtoken');

var userSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true
},
name: {
type: String,
required: true
},
hash: String,
salt: String
});

userSchema.methods.setPassword = function(password) {
this.salt = crypto.randomBytes(16).toString('hex');
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
};

userSchema.methods.validPassword = function(password) {
var hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
return this.hash === hash;
};

userSchema.methods.generateJwt = function() {
var expiry = new Date();
expiry.setDate(expiry.getDate() + 7);

return jwt.sign({
_id: this._id,
email: this.email,
name: this.name,
exp: parseInt(expiry.getTime() / 1000)
}, process.env.JWT_SECRET); // DO NOT KEEP SECRET IN CODE
};


=========================================================================

app_client/config/passport.js


var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var mongoose = require('mongoose');
var User = mongoose.model('User');

passport.use(new LocalStrategy({
usernameField: 'email'
},
function(username, password, done) {
User.findOne({ email: username }, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, {
message: 'Incorrect username.'
});
}
if (!user.validPassword(password)) {
return done(null, false, {
message: 'Incorrect password.'
});
}
return done(null, user);
});
}
));


=========================================================================

app_client/controllers/authentication.js


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

var sendJsonResponse = function(res, status, content) {
res.status(status);
res.json(content);
};

module.exports.register = function(req, res) {
if(!req.body.name || !req.body.email || !req.body.password) {
sendJsonResponse(res, 400, { "message" : "All fields required" });
return;
}

var user = new User();

user.name = req.body.name;
user.email = req.body.email;

user.setPasswork(req.body.password);

user.save(function(err) {
var token;
if (err) {
sendJsonResponse(res, 404, err);
} else {
token = user.generateJwt();
sendJsonResponse(res, 200, { "token" : token });
}
});
};

module.exports.login = function(req, res) {
if(!req.body.email || !req.body.password) {
sendJsonResponse(res, 400, { "message" : "All fields required" });
return;
}

passport.authenticate('local', function(err, user, info) {
var token;

if (err) {
sendJsonResponse(res, 404, err);
return;
}

if(user) {
token = user.generateJwt();
sendJsonResponse(res, 200, { "token" : token });
} else {
sendJsonResponse(res, 401, info);
}
})(req, res);

};


=========================================================================

app_client/routes/index.js


var express = require('express');
var router = express.Router();
var ctrlLocations = require('../controllers/locations');
var ctrlReviews = require('../controllers/reviews');
var ctrlAuth = require('../controllers/authentication');

// locations
router.get('/locations', ctrlLocations.locationsListByDistance);
router.post('/locations', ctrlLocations.locationsCreate);
router.get('/locations/:locationid', ctrlLocations.locationsReadOne);
router.put('/locations/:locationid', ctrlLocations.locationsUpdateOne);
router.delete('/locations/:locationid', ctrlLocations.locationsDeleteOne);

// reviews
router.post('/locations/:locationid/reviews', ctrlReviews.reviewsCreate);
router.get('/locations/:locationid/reviews/:reviewid', ctrlReviews.reviewsReadOne);
router.put('/locations/:locationid/reviews/:reviewid', ctrlReviews.reviewsUpdateOne);
router.delete('/locations/:locationid/reviews/:reviewid', ctrlReviews.reviewsDeleteOne);

// authentication
router.post('/register', ctrlAuth.register);
router.post('/login', ctrlAuth.login);

module.exports = router;


=========================================================================

That's all code relating to the new schema, API endpoint, and error message. I've tried moving things around in code (the ordering in app.js) and minor tweaking, but can't seem to track down the issue here. Like I said though, I am kind of new to the MEAN stack.

Answer

You're defined the user schema, but didn't define the model & didn't export it to the outer world.

Add to the app_client/models/users.js:

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

and (this step can be probably skipped) to the app_client/config/passport.js and app_client/controllers/authentication.js:

var User = require('../models/users.js');