Validation
Objectives
By the end of this lesson, you should be able to:
- Add validation rules to a Mongoose schema, beyond just
required - Run validation manually, and read the resulting error
- Explain what happens when an invalid document is saved
💡 Why this matters: Module 7’s native driver would happily insert a document missing required fields, or with a nonsense value in a numeric field, nothing would stop it. This is exactly the gap Mongoose validation closes.
⚠️ A note on verification: unlike most of this module, Mongoose’s validation runs entirely inside the Node.js process, no database connection needed at all, so the code and output below were actually run.
Validation Rules
const mongoose = require('mongoose');
const studentSchema = new mongoose.Schema({
name: { type: String, required: true },
grade: { type: Number, required: true, min: 1, max: 12 },
email: {
type: String,
match: /^\S+@\S+\.\S+$/
}
});
const Student = mongoose.model('Student', studentSchema);
Beyond required, this schema adds min/max (a valid range for grade) and match (a regular expression email must satisfy).
Validating Manually
Every Mongoose document has a .validate() method, running every rule in the schema without needing a database connection at all:
const valid = new Student({ name: 'Erin', grade: 9, email: 'erin@example.com' });
try {
await valid.validate();
console.log('Valid, no errors');
} catch (err) {
console.log(err.message);
}
Valid, no errors
A Missing Required Field
const missingName = new Student({ grade: 9 });
try {
await missingName.validate();
} catch (err) {
console.log(err.errors.name.message);
}
Path `name` is required.
A Value Outside the Allowed Range
const badGrade = new Student({ name: 'Jordan', grade: 15 });
try {
await badGrade.validate();
} catch (err) {
console.log(err.errors.grade.message);
}
Path `grade` (15) is more than maximum allowed value (12).
A Value That Fails a Pattern
const badEmail = new Student({ name: 'Maya', grade: 9, email: 'not-an-email' });
try {
await badEmail.validate();
} catch (err) {
console.log(err.errors.email.message);
}
Path `email` is invalid (not-an-email).
Validation Runs Automatically on save() and create()
Calling .validate() directly (as above) is useful for understanding what’s happening, but in practice, Student.create({...}) and document.save() both run this exact same validation automatically, before anything is written to the database, and reject with the same kind of error if it fails. An invalid document, missing a required field or violating min/max/match, never actually reaches MongoDB.
Comparing to Prisma
This is Mongoose’s version of what a required field, @unique, or a check constraint would enforce in a Prisma schema (Module 4), except Prisma’s enforcement happens at the database level (a real PostgreSQL constraint), while Mongoose’s validation happens in the application layer, before the write is even attempted. Both catch bad data, at different points in the stack.
Try It
- Add
min,max, ormatchvalidation to a schema of your own, and confirm valid data passes with.validate(). - Confirm a document missing a required field produces a
requirederror, matching the pattern shown above. - Confirm a document violating
min/maxormatchproduces a clear, field-specific error message. - Explain, in your own words, the difference between Mongoose validation (application-level) and a PostgreSQL constraint (database-level).
Recap
- Mongoose validation rules (
required,min,max,match, and more) run entirely inside the application, no database connection needed. .validate()can be called manually, and runs automatically beforecreate()orsave()write anything to the database.- Validation failures produce a structured error object, with a specific message per invalid field, accessible through
err.errors.<fieldName>.message.
This is the final lesson of this module before exercises. Next module: modeling relationships between MongoDB documents, embedding versus referencing.