CodingNic

Mongoose: Schemas and Models

Validation

Mongoose: Schemas and Models 15 min read

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

javascript
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:

javascript
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);
}
text
Valid, no errors

A Missing Required Field

javascript
const missingName = new Student({ grade: 9 });
try {
  await missingName.validate();
} catch (err) {
  console.log(err.errors.name.message);
}
text
Path `name` is required.

A Value Outside the Allowed Range

javascript
const badGrade = new Student({ name: 'Jordan', grade: 15 });
try {
  await badGrade.validate();
} catch (err) {
  console.log(err.errors.grade.message);
}
text
Path `grade` (15) is more than maximum allowed value (12).

A Value That Fails a Pattern

javascript
const badEmail = new Student({ name: 'Maya', grade: 9, email: 'not-an-email' });
try {
  await badEmail.validate();
} catch (err) {
  console.log(err.errors.email.message);
}
text
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

  1. Add min, max, or match validation to a schema of your own, and confirm valid data passes with .validate().
  2. Confirm a document missing a required field produces a required error, matching the pattern shown above.
  3. Confirm a document violating min/max or match produces a clear, field-specific error message.
  4. 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 before create() or save() 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.