Bad Data Is No Better Than No Data: Then, Validate Ebenezer!
After my previous post, Noding, MongoDBing With Mongoose: On the Account of the MVA and Visual Studio Code, the natural thing to do was to validate the data model before a save or an update. Let us get to it then!
It is always good practice to validate application data in the logic layer but it should be a hard requirement for any solution to enforce data validation on the model layer just before the data is saved. MongoDB does not provide data validation, therefore data validation is on us; we are responsible for it.
Using the same Mongoose Data Model defined in the previous note, let us see how many things can go wrong with our data before adding data validation. So, we had BankData with a collection of Accounts. Can BankData be saved, updated without a FirstName or LastName? Can a new Account be added without a type? Or perhaps add a negative amount to an account? Oh, yes, it can be done! The Mongoose Schema has no validation! That's not good; darn bank!
I get it, I need validation, so what types of validation Mongoose offers? First off, validation is defined in the schema and it takes place when a document attempts to be saved, after default values have been applied . Therefore, if our Schema would have contain validation then the above saves, without changing the code at all, would have never work and the error would have been passed to the callback, which means that assert.equals(null, err) would have failed.
If the client tries save the bad data then Mongoose, before the save, executes the validation, detects the error and the data is not saved. Nice! But there is more, the String type take s list of allowed values therefore I could place here the currencies allowed by the bank, say USD and CAD only.
Line 13 above is passing a validation function and an error type to the model SchemaType. The currency could be anything but GBP. This is a bit of a problem because now anything goes but the little £; even made up currencies could be set which is truly wrong.
Using the same Mongoose Data Model defined in the previous note, let us see how many things can go wrong with our data before adding data validation. So, we had BankData with a collection of Accounts. Can BankData be saved, updated without a FirstName or LastName? Can a new Account be added without a type? Or perhaps add a negative amount to an account? Oh, yes, it can be done! The Mongoose Schema has no validation! That's not good; darn bank!
| The client was able to save BankData with no first_name and no last_name and later on it was updated with a new account with no type. |
I get it, I need validation, so what types of validation Mongoose offers? First off, validation is defined in the schema and it takes place when a document attempts to be saved, after default values have been applied . Therefore, if our Schema would have contain validation then the above saves, without changing the code at all, would have never work and the error would have been passed to the callback, which means that assert.equals(null, err) would have failed.
Built-in SchemaType Validation
Luckily for me built in validation into the SchemaField can be applied to make these fields required.If the client tries save the bad data then Mongoose, before the save, executes the validation, detects the error and the data is not saved. Nice! But there is more, the String type take s list of allowed values therefore I could place here the currencies allowed by the bank, say USD and CAD only.
Custom Schema Validation
But, what about if this bank accepts all currencies of the world,except... the British Pound! then the above validation would be unreasonable because the enum array for currency would be really, really long. Ah! Instead we could use a custom validation and test only for the invalid ones! How does that work? Easy!Line 13 above is passing a validation function and an error type to the model SchemaType. The currency could be anything but GBP. This is a bit of a problem because now anything goes but the little £; even made up currencies could be set which is truly wrong.
Mongoose Middleware
Using Mongoose middleware we can validate the account currency type by intercepting the save process in a pre-save event and test the currency set by the client against a collection currencies previously set in MongoDB. A middleware is like an interceptor which inserts its execution before or after the process execution. The pre-save would be like catching the save on the fly. This would be useful for the execution of complex validation or for setting some properties of the object just before save.![]() |
| A Pre-save middleware executes before the save operation and if everything is OK what happens next is the actual save. |
The way to implement a pre-save middleware for the account is like this:
A few things to notice from the above middleware; First, there is a new Mongoose Schema, the validCurrencyModel (line 2). This model represents a valid currency used by the bank. The bank collection of valid currencies might not change very often but I decided to store it in the database for better management. I'll show you how I batch load them to the MongoDB in a little later.
Note that in line 4 the middleware is executing a query for currency of the account (this.currency) on Currency to find out if the currency set by the client is valid. If the currency is not found then an error is issued of the type InvalidCurrency and the next thing that happens is that the error is returned to the callback of the save (line 11). If the currency type is valid then the middle ware calls next() which means "carry on to the next step, the save, this data is good".
Batch-Loading Currency
A Currency object was created. Note three things in the following code section: currency_symbol is required, has custom validation and is unique.
Note that the batch was done by creating an array of Currency and passing it to create. Create returns to the callback the error and the entire collection of created documents. Simple.
Conclusion
In this note I have used Mongoose validation in three different ways to validate the Data Model:- Validation at SchemaType level. We can use required, allowed value enums for strings. Max and Min for Number types. In the case of Currency the SchemaType for currency_symbol was also set to "unique: true" in order to avid duplicates.
- Custom Validation: By passing a validation function to the SchemaType
- Using Mongoose Middleware: Validation of the model in a pre-save event.
This note shows only a taste on Mongoose validation. You have probably noticed that to test my Mongoose model I have been using scripts which are not very useful. I feel that the natural continuation of this Node + MongoDB + Mongoose would be to use the defined model in a system through a RESTful API using Express... hum, I would not be surprised if I soon take on this.
References
- MongoDB Validate Command: This command checks the structures within a namespace for correctness by scanning the collection’s data and indexes. The command returns information regarding the on-disk representation of the collection and does not validate the data.
- Mongoose Validation
- Noding, MongoDBing With Mongoose: On the Account of the MVA and Visual Studio Code

No comments:
Post a Comment