Mongoose Validation Examples

Mongoose Validation Examples

Just like all other frameworks, Mongoose provides a way to validate data before you save that data to a database. Data validation is important to make sure that “bad” data does not get persisted in your application. A benefit of using Mongoose when inserting data into MongoDB is its built-in support for data schemas, and the automatic validation of data when it is persisted. You would not get this without Mongoose. Mongoose’s validators are easy to configure. When defining the schema, a developer can add extra options to the property that should be validated. Let’s look at some basic examples of validation in Mongoose now.

Getting Started With required

Right now we have a schema in Mongoose which has no validation. In other words, all the properties defined below are optional. If you provide each property when creating a document, great! If not, that’s great too!

Of course it is almost always necessary to Validate any data you want to persist. We can modify the schema to add validation like so. In the snippet below, we are making the title of the game mandatory. It is no longer optional. We can do this with the required property.

With our validation in place for the title of the game, let’s try to save a game to the database without specifying a title.

We can run index.js using node index.js at the terminal to test this out.

mongo-crud $node index.js
(node:10176) UnhandledPromiseRejectionWarning: Unhandled promise rejection 
(rejection id: 2): ValidationError: Game validation failed: title: Path title 
is required. (node:10176) [DEP0018] DeprecationWarning: Unhandled promise rejections 
are deprecated. In the future, promise rejections that are not handled will 
terminate the Node.js process with a non-zero exit code.

Interesting. We get a lot of error information about an unhandled promise rejection. We can fix this by updating our logic in the saveGame() function. The good thing however is that within the information listed we do see that the validation worked as Game validation failed: title: Path title is required tells us so. Let’s update the code to handle the promise correctly by implementing a try/catch block.

Running the index.js file now give us an easier to read message.

mongo-crud $node index.js
Game validation failed: title: Path title is required.

Great! Validation is working. It is important to note that this example of validation in Mongoose is just that, validation in Mongoose. This has nothing to do with data validation at the database, or MongoDb level. Another thing to note is that this type of validation in Mongoose is complimentary to a validation package like Joi which we used in the node rest api tutorial. By using both validation at the REST layer and the Mongoose layer, you can ensure that faulty documents will not be persisted to the database.

More About Built In Validators

In the section above we saw how to use the required property to make it mandatory that a user provide the title of a game when persisting to the database. You can also use a function with required to conditionally require something. In the snippet below, we are saying that if the game is on sale, then the price is required.

Now we can try to save a game, but we will omit the title and price to see if our validation rules are still working. Our logic to insert the game is here.

Running the program shows that these rules are working well. Both price and title are required, so the game is not persisted.

mongo-crud $node index.js
Game validation failed: price: Path price is required., title: Path title is required.

minlength and maxlength

In addition to making a string required, you can also specify the minimum length and maximum length it should be. Consider this schema.

Now, lets provide a title, but with only 3 characters and see what happens.

When we run the program the validator tells us that our title is too short.

mongo-crud $node index.js
Game validation failed: price: Path price is required., 
title: Path title (Pac) is shorter than the minimum allowed length (4).

enum validation

When creating a game, we are assigning some tags to it. Using enum validation, we can specify the available tags one could use. Below we are saying that the tags for a game must be any of sports, racing, action, or rpg.

Now we try to save a game using a tag we have not accounted for in the enum validation, adventure.

Sure enough, trying to insert that game into the database fails and we get the error that adventure is not a valid enum value for path tags.

mongo-crud $node index.js Game validation failed: tags.0: adventure is not a valid enum value for path tags.

Custom Validators

You may also set up a custom validator in Mongoose. Here we will modify the validation for tags such that a user must provide more than one.

Now we try to save a game and only provide one tag.

Running the program gives us the validation error we expect.

mongo-crud $node index.js
Game validation failed: tags: You must provide more than 1 tag.

Async Valicators

Async validation comes into play when you need to fetch some remote data, or perform some other type of asynchronous task before persisting to the database. For this we can use an async validator. Let’s have a look at one. We’ll simulate asynchronous work with the setTimeout() function.

To enable asynchronous validation, all you need to do is add the isAsync property to the validate object and set it to true. Then you can do your async work whether that be fetching remote data, reading from the filesystem, or working with a database, and the validation will still work properly.

Mongoose Validation Examples Summary

In this tutorial on Mongoose Validation we learned that when defining a schema, you can set the type of a property to a SchemaType object. You use this object to define the validation requirements for the given property. We can add validation with code like this.

Validation logic is executed by Mongoose before a document can be saved to the database. It is also possible to trigger it manually by calling the validate() method. Some of the Built-in validators include:

  • Strings: minlength, maxlength, match, enum
  • Numbers: min, max
  • Dates: min, max
  • All types: required

To set up custom validation, you may set up the validate object and use a function in the validate property.

When talking to a database or a remote service to perform the validation, it is required that you use an async validator. You enable this with the isAsync property set to true.

Some other useful SchemaType properties include:

  • Strings: lowercase, uppercase, trim
  • All types: get, set (to define a custom getter/setter)

Happy Validating!