Days 32-33: Introduction to Mongoose – Models, Schemas, and Data Validation

Days 32-33: Introduction to Mongoose – Models, Schemas, and Data Validation

Mongoose is an Object Data Modeling (ODM) library for MongoDB that simplifies interactions with the database in Node.js applications. It provides a structured way to define database schemas, create models, and enforce data validation.

MongoDB is a schema-less NoSQL database, meaning it doesn’t enforce any predefined structure on documents. However, in large-scale applications, maintaining data consistency is crucial. This is where Mongoose schemas and validation help by ensuring that data follows a strict format before being stored in the database.

In this guide, we will explore Mongoose models, schemas, and data validation techniques with real-world examples.


🛠 Step 1: Understanding Mongoose Models and Schemas

🔹 What is a Schema?

A Mongoose schema defines the structure, data types, and validation rules for documents stored in a MongoDB collection. Think of it as a blueprint for how data should be stored.

Example: Defining a User Schema

const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
  name: { type: String, required: true }, // Name is required
  age: Number, // Age is optional
  email: { type: String, unique: true, required: true } // Email must be unique and required
});        

📌 Explanation:

  • name: A required string field.
  • age: A number field (optional).
  • email: A required and unique string field.


🔹 What is a Model?

A model in Mongoose is an interface to interact with MongoDB collections. It allows us to perform CRUD operations (Create, Read, Update, Delete) on documents.

Example: Creating a Model

const User = mongoose.model("User", userSchema);        

📌 Explanation:

  • User is a model based on the userSchema.
  • It maps to the "users" collection in MongoDB (Mongoose automatically pluralizes model names).


🔹 Saving a New Document

Once we have the model, we can create and save new user data to the MongoDB database.

const newUser = new User({
  name: "Alice",
  age: 25,
  email: "alice@example.com"
});

newUser.save()
  .then(() => console.log("User saved!"))
  .catch(err => console.error("Error:", err));        

📌 Key points:

  • save() is used to insert a new document into the database.
  • .then() handles the success response.
  • .catch() captures and logs any errors.

🔹 What happens if we try to save a user without an email?

const invalidUser = new User({ name: "Bob", age: 30 });

invalidUser.save()
  .then(() => console.log("User saved!"))
  .catch(err => console.error("Error:", err));        

Since email is required, this operation will fail, and an error message will be logged.


🛠 Step 2: Data Validation and Schema Design in Mongoose

Mongoose provides built-in and custom validation techniques to enforce data integrity. Let’s explore different ways to validate data before storing it in the database.


🔹 Adding Field Validations

Mongoose allows us to define constraints like:

✅ required - Ensures the field is not empty.

✅ minlength & maxlength - Sets character length limits for strings.

✅ match - Uses regex patterns for format validation.

✅ enum - Limits values to a predefined set.

✅ min & max - Defines numerical range constraints.

Example: Validating User Schema

const userSchema = new mongoose.Schema({
  name: { type: String, required: true, minlength: 3 }, // Minimum length of 3 characters
  email: { type: String, required: true, match: /.+\@.+\..+/ }, // Must match an email pattern
  age: { type: Number, min: 18, max: 65 }, // Age must be between 18 and 65
  role: { type: String, enum: ["admin", "user", "guest"], default: "user" } // Role can only be admin, user, or guest
});        

📌 Key Takeaways:

required: true ensures the field is not empty.

minlength: 3 ensures names have at least 3 characters.

match: /.+\@.+\..+/ ensures the email follows a proper format.

min: 18, max: 65 ensures age is within the allowed range.

enum limits the role field to specific values.


🔹 Using Default Values

We can assign default values to fields when they are not provided.

Example: Setting Default Values

const userSchema = new mongoose.Schema({
  isActive: { type: Boolean, default: true }
});        

Now, if a user document is created without specifying isActive, it will automatically be set to true.


🔹 Custom Validation

For advanced cases, we can write custom validation functions.

Example: Custom Age Validation

const userSchema = new mongoose.Schema({
  age: {
    type: Number,
    validate: {
      validator: function(value) {
        return value >= 18;
      },
      message: "Age must be at least 18."
    }
  }
});        

📌 How it works:

✔ The validator function checks if age >= 18.

✔ If validation fails, the custom error message is returned.


🔹 Pre-save Hooks (Middleware)

Mongoose allows us to run functions before saving documents using middleware.

Example: Hashing Passwords Before Saving

const bcrypt = require("bcrypt");

const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  password: { type: String, required: true }
});

userSchema.pre("save", async function (next) {
  if (!this.isModified("password")) return next();
  this.password = await bcrypt.hash(this.password, 10);
  next();
});        

📌 Why use this?

✔ Ensures that passwords are hashed before being stored in the database.

✔ Prevents plain-text password storage, improving security.

Manu D

Jr. UI/UX design and development | Figma |HTML & CSS | Wordpress Developer | Graphic Designer | Web Designer

2mo

Very informative

Like
Reply

To view or add a comment, sign in

More articles by Thirumoorthi M

Insights from the community

Others also viewed

Explore topics