Skip to content

Instantly share code, notes, and snippets.

@stongo
Last active September 7, 2024 14:28
Show Gist options
  • Save stongo/6359042 to your computer and use it in GitHub Desktop.
Save stongo/6359042 to your computer and use it in GitHub Desktop.
Joi validation in a Mongoose model
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var db = mongoose.connection;
db.on('error', function() {
return console.error.bind(console, 'connection error: ');
});
db.once('open', function() {
var User;
return User = require('./user.js');
});
// Validate a user
(function() {
var User = require('./user.js');
var me = { username: 'foo' };
var user = new User(me);
var err = user.joiValidate(me);
if (err) throw err;
user.save(function(err, saved) {
...
});
})();
var userSchema = mongoose.Schema({
username: String,
password: String,
email: String,
first_name: String,
last_name: String,
created: { type: Date, default: Date.now },
});
userSchema.methods.joiValidate = function(obj) {
var Joi = require('joi');
var schema = {
username: Joi.types.String().min(6).max(30).required(),
password: Joi.types.String().min(8).max(30).regex(/[a-zA-Z0-9]{3,30}/).required(),
email: Joi.types.String().email().required(),
first_name: Joi.types.String().required(),
last_name: Joi.types.String().required(),
created: Joi.types.Date(),
}
return Joi.validate(obj, schema);
}
module.exports = mongoose.model('User', userSchema);
@dhavaljardosh
Copy link

Spend 4 days to understand Joi with Mongoose, even tried Joigoose (it had several limitations) and finally found this. This is very clear. Thank you very much.

@JeffLabonte
Copy link

Good thinking! Good job! Thank you for sharing!

@sagar-gavhane
Copy link

Combination of Joi and Mongoose is great...

@meteorlxy
Copy link

meteorlxy commented Aug 20, 2018

userSchema.statics.joiValidate = ...

Could be another choice

@cicconewk
Copy link

Good job! It helped me a lot 👍.

I think it is better to use the validator method before creating a new user schema.

@deoncasas
Copy link

Nice validation for mongoose make my life easier

@srujanpurohit
Copy link

The problem with this approach is that if you need some fields only for Joi validation, while creating schema mongoose will remove those field. For example in this case, what if there is another field for "repeat password" that is not being saved in db but is required for validation.

@devChedar
Copy link

If you are using express better method would be to add a middleware for validation so that the problem mentioned by @srujanpurohit doesn't exist. So instead it would look something like this

// user Model

const mongoose = require('mongoose')
const Joi = require('joi')

const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: [true, 'Please enter a email'],
    unique: true,
    lowercase: true,
  },
  password: {
    type: String,
    required: [true, 'Please enter a password'],
    minlength: 8,
  },
})

const User = mongoose.model('user', userSchema)

const validateUser = (user) => {
  const schema = Joi.object({
    email: Joi.string().email().min(5).max(500).required(),
    password: Joi.string().min(8).max(1024).required(),
  })
  return schema.validate(user)
}
module.exports = {
  User,
  validateUser,
}

// validateMiddleware
module.exports = (validator) => {
  return (req, res, next) => {
    const { error } = validator(req.body)
    console.log('error: ', error)
    if (error) {
      return res.status(400).send(error.details[0].message)
    }
    next()
  }
}
// authRoute

const { validateUser } = require('../models/User')
const validateMiddleWare = require('../middleware/validate')
const authController = require('../controllers/authController')

router.post('/signup', [validateMiddleWare(validateUser)], authController.signup_post)

@deevally
Copy link

If you are using express better method would be to add a middleware for validation so that the problem mentioned by @srujanpurohit doesn't exist. So instead it would look something like this

// user Model

const mongoose = require('mongoose')
const Joi = require('joi')

const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: [true, 'Please enter a email'],
    unique: true,
    lowercase: true,
  },
  password: {
    type: String,
    required: [true, 'Please enter a password'],
    minlength: 8,
  },
})

const User = mongoose.model('user', userSchema)

const validateUser = (user) => {
  const schema = Joi.object({
    email: Joi.string().email().min(5).max(500).required(),
    password: Joi.string().min(8).max(1024).required(),
  })
  return schema.validate(user)
}
module.exports = {
  User,
  validateUser,
}
// validateMiddleware
module.exports = (validator) => {
  return (req, res, next) => {
    const { error } = validator(req.body)
    console.log('error: ', error)
    if (error) {
      return res.status(400).send(error.details[0].message)
    }
    next()
  }
}
// authRoute

const { validateUser } = require('../models/User')
const validateMiddleWare = require('../middleware/validate')
const authController = require('../controllers/authController')

router.post('/signup', [validateMiddleWare(validateUser)], authController.signup_post)

Awesome. Thanks for this. Really helped.

@Dakudbilla
Copy link

@stongo Great job. Really helpful.

@devChedar I love your approach too

@rukundo-kevin
Copy link

@stongo Great work. You saved me for real.

@clementrugwiro
Copy link

helpful indeed

@danwhitston
Copy link

Thanks for the write-up @stongo! The example by @devChedar works nicely. However, it gets slightly more complex when you use the same validation for multiple actions with different validation requirements. For example:

  • Saving a user requires valid username, email, password
  • Logging in as a user requires only email and password

You can make username not required in Joi validation, but since it is actually required for registration, you're then reliant on existence validation getting kicked down the road to the mongoose schema. You can also just not run Joi validation on login requests, but that's also less than ideal.

I suspect the 'best' solution is to create different validation functions for actions with different validation requirements, e.g. validateUserWrite and validateUserLogin. At least, that's how I'm going to handle it. Thanks again for the work on putting together a clean way of doing this.

@phovious-14
Copy link

thanks

@BlossomDugbatey
Copy link

Thank you, this was helpful.

@rizkiramadhanx
Copy link

Thanks broo

@abhijith-dev
Copy link

thanks this was very helpful 👍

@jbae9
Copy link

jbae9 commented Dec 21, 2022

@devChedar I know this is an old thread but just wanted to let you know this helped me out 2 years after you posted👍👍👍

@Raph2ll
Copy link

Raph2ll commented Jan 1, 2023

Thanks @stongo, this thread helped me a lot.

@hidaytrahman
Copy link

Thank you, this was helpful.

@shashwat-idm
Copy link

@danwhitston Precisely. There's no way around it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment