Learning JWT Part 4 - Mongoose Validation === ![](https://i.imgur.com/qFSfCl5.png) --- ###### tags: `JWT` In last chapter, we have signned up an user successfully, let's take a look at the error message if we do not pass any properties. The response would be like this. ![](https://i.imgur.com/MJcSFGz.png) Error message in node. ![](https://i.imgur.com/8rw3J3a.png) In order to have a better user experience, it's always good to have some concise and direct error message for user to understand what went wrong. --- ### Mongoose Validator Mongoose has built-in validators: 1. **All SchemaTypes** have the built-in required validator. The required validator uses the SchemaType's **checkRequired()** function to determine if the value satisfies the required validator. 2. Numbers have **min** and **max** validators. 3. Strings have **enum**, match, **minLength**, and **maxLength** validators. #### Custom Error Messages > Reference: [Validation errors](https://mongoosejs.com/docs/validation.html#validation-errors) There're two equivalent ways to set the validator error message, i.e.: - Array syntax: `min: [6, 'Must be at least 6, got {VALUE}']` - Object syntax: `enum: { values: ['Coffee', 'Tea'], message: '{VALUE} is not supported' }` Let's add some custom error message. ```javascript= // User.js const userSchema = new mongoose.Schema({ email: { type: String, required: [true, "Please enter an email"], unique: true, lowercase: true, validate: [true, "Please enter a valid email"] }, password: { type: String, required: [true, "Please enter password"], minlength: [6, "Minimum password length is 6 characters"] } }); ``` As for **Email**, there're several ways to validate an email: 1. Use Regex - `/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/` ```javascript= // Here is a function to validate an email const validateEmail = (emailText) => { const mailFormat = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; if (emailText.value.match(validateEmail)) { // do something... } else { // do something... } } ``` 2. Use a package validator - [validator](https://www.npmjs.com/package/validator) ```javascript= // require validator import validator from 'validator'; const vaidateEmail = (emailText) => { if (validator.isEmail(emailText)) { // do something... } else { // do something... } } ``` Here we use package **validator** to varify email. ```javascript // User.js const { isEmail } = require("validator"); const userSchema = new mongoose.Schema({ email: { type: String, required: [true, "Please enter an email"], unique: true, lowercase: true, validate: [isEmail, "Please enter a valid email"] }, password: { type: String, required: [true, "Please enter password"], minlength: [6, "Minimum password length is 6 characters"] } }); ``` Now that we have add custom error messages, we can give it a try, let's pass wrong format of an email and password. Open Postman, try to pass wrong data. ```json! { "email": "test-1@gmail", "password": "11" } ``` Open terminal, you should be able to see custom error messages we have added like below.n ![](https://i.imgur.com/1iAB0xO.png) But w still got response like this saying that **User note create**, that's because in **authController**, we have specified the error message in try catch block. ![](https://i.imgur.com/aFx68Gm.png) ```javascript! module.exports.signup_post = async (req, res) => { const { email, password } = req.body; try { // Properties passed here must be matched in the User schema const user = await User.create({ email, password }); res.status(201).json(user); } catch (error) { // make in a seperate file as an utility function // then store the error object as a variable so that // we can pass into .json(error) handleError(error); res.status(400).json("User not create!"); } }; ``` Here we can try to pass an error object that contains our custom error message so that the error message would be more specific to users. First of all, let's create an error handle function in **utility** folder. - Create **utility** folder. - Create **handleError.js** file. Let's console.log the error message and error.code, follow the steps below: - Open Postman. - Selet **POST** request and localhost would be `http://localhost:3000/signup`, change port depends on your setting. - Check terminal to see the result. ```javascript // handleError.js const handleError = (err) => { console.log(err.message, err.code) } module.export = handleError; ``` Below is the result of the err.message and the err.code, as you can see the err.code is `undefined` ![](https://i.imgur.com/82g6nG0.png) Now, let's console.log ***err* to see the data structure. ```javascript // handleError.js const handleError = (err) => { console.log(err) } module.export = handleError; ``` As you can see the err has 2 layers, the first layer is **Error**, inside of **Error** has another layer **errors**, we can access **errors** to get the value of **email** and **password**. ![](https://i.imgur.com/0faZux8.png) Let's use `Object.value` to get the information from Error. ```javascript! // handleError.js const handleError = (err) => { // create an error object const errors = { email: "", password: "" } // validatate error // Need to make sure this error is validated. if (err.message.includes("user validation failed")) { console.log(Object.value(err.errors)) } } module.export = handleError; ``` `Object.value()` returns an array of a given object's own enumerable string-keyed property values, therefore you can see the red square presents as an **array**, now we can loop through it and get its property. ![](https://i.imgur.com/Lu2umDa.png) ```javascript= // handleError.js const handleError = (err) => { // create an error object const errors = { email: "", password: "" } // validatate error // Need to make sure this error is validated. if (err.message.includes("user validation failed")) { Object.value(err.errors).forEach(error => { console.log(error.properties) }) } } module.export = handleError; ``` We can destructure it to get properties without using `error.properties` ```javascript= // handleError.js const handleError = (err) => { // create an error object const errors = { email: "", password: "" } // validatate error // Need to make sure this error is validated. if (err.message.includes("user validation failed")) { Object.value(err.errors).forEach(({properties}) => { console.log(properties) }) } } module.export = handleError; ``` ![](https://i.imgur.com/cKrLHte.png) Now we've got values from properties, we can update **erros**, earlier, so the constant is `let errors = { email: "", password: "" }`, we can access `path` as keys, and message as it contain the custom errors we need and return **errors**. ```javascript= // handleError.js const handleError = (err) => { // create an error object const errors = { email: "", password: "" } console.log(error.code); // validatate error // Need to make sure this error is validated. if (err.message.includes("user validation failed")) { Object.value(err.errors).forEach(({properties}) => { errors[properties.path] = properties.message; }) } return errors; } module.export = handleError; ``` Let's go back to `authController.js` ```javascript= // authController.js module.exports.signup_post = async (req, res) => { const { email, password } = req.body; try { const user = await User.create({ email, password }); res.status(201).json(user); } catch (error) { // Store the result and set as a constant // pass errors constant to json() const errors = handleError(error); res.status(400).json({errors}); } }; ``` Now the error messages are much clear and we can render on our interface. ![](https://i.imgur.com/P4X1T7y.png) --- #### handle duplicated registration Now that we can use our custom error message to notify user, but let's take a look at what will happen if we try to register using the same email. ![](https://i.imgur.com/3aNtyhf.png) Here is the **error.code** from `handleError.js`, the reason why we've got this error message was because we have specified **unique: true** in our `User.js` schehma. We can set a condition says that if the error codde is equals to 11000, then we make another custom error message. ![](https://i.imgur.com/kTSI3rn.png) ```javascript= // handleError.js const handleError = (err) => { // create an error object const errors = { email: "", password: "" } if (error.code === 11000) { errors.email = "This email is already registered." return errors; } // validatate error // Need to make sure this error is validated. if (err.message.includes("user validation failed")) { Object.value(err.errors).forEach(({properties}) => { errors[properties.path] = properties.message; }) } return errors; } module.export = handleError; ``` Let's try to test again, now we can see the currect error message. ![](https://i.imgur.com/ErtuYSv.png)