Skip to content

Instantly share code, notes, and snippets.

@stongo
Last active September 7, 2024 14:28
Show Gist options
  • Select an option

  • Save stongo/6359042 to your computer and use it in GitHub Desktop.

Select an option

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);
@srujanpurohit

Copy link
Copy Markdown

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
Copy Markdown

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
Copy Markdown

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
Copy Markdown

@stongo Great job. Really helpful.

@devChedar I love your approach too

@rukundo-kevin

Copy link
Copy Markdown

@stongo Great work. You saved me for real.

@clementrugwiro

Copy link
Copy Markdown

helpful indeed

@danwhitston

Copy link
Copy Markdown

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
Copy Markdown

thanks

@BlossomDugbatey

Copy link
Copy Markdown

Thank you, this was helpful.

@rizkiramadhanx

Copy link
Copy Markdown

Thanks broo

@abhijith-dev

Copy link
Copy Markdown

thanks this was very helpful ๐Ÿ‘

@jbae9

jbae9 commented Dec 21, 2022

Copy link
Copy Markdown

@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

Raph2ll commented Jan 1, 2023

Copy link
Copy Markdown

Thanks @stongo, this thread helped me a lot.

@hidaytrahman

Copy link
Copy Markdown

Thank you, this was helpful.

@shashwat-idm

Copy link
Copy Markdown

@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