Adding Validation for Embedded Array in Array Objects in Mongoose
Surprisingly there was very little information I could find when googling this issue. Granted, it’s a very niche issue but here’s what I personally found when trying things out. If you’re unfamiliar with mongoose’s validation please consult the guide first. This document assumes some familiarity with mongoose schemas, embedded schemas, and validation.
There’s a lot of pre-explanation. if you want to go directly to the answer head to the end of the page
So in mongoose, there’s several ways to define a schema. Once a Schema is defined, you can require mongoose to put custom validation on the object before it gets saved into the database. Mongoose documentation recommends you use the Schema#path(path, constructor)
function to get the SchemaType object where you can then use SchemaType#validate(obj, [errorMsg])
on. The problem occurs when your path branches into an array.
The following examples are attempting to model a Car object that can have one or more features.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
the following below creates two schemas, car and features, and puts a reference of features in cars similar to how typical RDBMS work.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Each method of creating mongoose objects has some benefits and tradeoffs.
Embedded Object in Single Schema
Pros
- data access is simpler, requires no joining of documents
- less boilerplate code to write
Cons
- If data is large, can get messy
- Not possible to set SchemaType options directly in Schema
Objects split between multiple Schemas
Pros
- Similar to RDBMS normalization structure
- data is more scalable and reusable
- Mongoose supports the addition of validation through SchemaType options in the Schema declaration itself
Cons
- Increased amount of boilerplate code needed for Schema creation.
- filesystem can get messy with large amount of tables required for a simple object.
Lets talk about adding validation
Lets say that we’re trying to add a ‘required’ validation. This validation will check to see if the Car object has a features array with a feature in it. If it doesn’t, the save will fail, if it does, then the document will be stored in the database.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
When embedded objects are used to define a schema, you first have to use Schema#path(path, constructor)
to get the Schema object and then use its validate function.
As for the second example, the validation is already set with the ‘required’ option being set on the ‘features’ attribute. By not declaring the Schema internally, you can place options such as ‘required’ and ‘validate’ in the Schema declaration. Refer to this page to see what other SchemaType
options there are.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Adding validation to a Schema with an array in an array
What if you had an embedded Schema that declared an embedded object in an array that declared another embedded object inside of another array. This is the tricky part that’s not very well documented.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
You’re unable to specify the path when the object is behind arrays. Accessing the Schema in the array itself cannot be accessed like CarSchema.path(‘features.model.year’)
, but it can be accessed by grabbing the schema
attribute which returns the SchemaType
of the object in the array. Now that we have the SchemaType
object we apply its path to continue the chain until we get the attribute that we want to apply the validation for. As far as I know, the DocumentArray.schema
attribute is not a documented feature.