059
LVL 03 — MID DEVELOPER SESSION 059 DAY 59

MONGOOSE SCHEMAS

🎫 PIXELCRAFT-046
🐛 Bug | 🟡 Medium | Priority: 🟠 High

Someone inserted a document without a name field. Another has width: "banana". The gallery crashes on both. Define what valid data looks like.
CONCEPTS.UNLOCKED
🦊
Mongoose
ODM (Object Document Mapper) for MongoDB. Maps JavaScript objects to MongoDB documents. Adds schemas, validation, and a clean API on top of the raw driver.
📐
Schemas
Define document structure, types, and constraints. name: String, width: Number, format: enum. The schema IS the documentation — it tells you exactly what a document looks like.
🛡
Validation
Required fields, min/max, enums, custom validators. required: [true, 'Name is required'], min: [1, 'Width must be at least 1']. Invalid data is rejected with clear messages.
🏗
Models
Image.create(), Image.find(), Image.findById() — the Model is your interface to the collection. Cleaner API than raw MongoDB driver. Validation runs automatically.
Middleware (Hooks)
pre('save'), post('save') — run code before/after database operations. Hash passwords before saving. Log changes after updating. Like Express middleware, but for the database.
🔗
References & Population
owner: { type: ObjectId, ref: 'User' } — reference documents in other collections. Like a foreign key. Populate to load the referenced document.
HANDS-ON.TASKS
01
Install Mongoose & Connect
npm install mongoose
02
Define the Image Schema
const mongoose = require('mongoose'); const imageSchema = new mongoose.Schema({ name: { type: String, required: [true, 'Image name is required'], trim: true, maxlength: [200, 'Name cannot exceed 200 characters'], }, width: { type: Number, required: true, min: [1, 'Width must be at least 1'], }, height: { type: Number, required: true, min: [1, 'Height must be at least 1'], }, format: { type: String, enum: ['png', 'jpeg', 'webp', 'gif'], default: 'png', }, filters: [{ name: String, value: Number, }], owner: { type: mongoose.Schema.Types.ObjectId, ref: 'User', // Reference to User model }, tags: [String], fileSize: Number, }, { timestamps: true, // Adds createdAt and updatedAt automatically }); const Image = mongoose.model( 'Image', imageSchema );
Every field has a type. Required fields reject documents without them. Enums only allow specific values. min/max enforce numeric ranges. timestamps adds createdAt/updatedAt automatically.
03
Replace Raw MongoDB with Mongoose
app.post('/api/images', async (req, res) => { try { const image = await Image.create(req.body); res.status(201).json(image); } catch (error) { if (error.name === 'ValidationError') { res.status(400) .json({ error: error.message }); } else { res.status(500) .json({ error: 'Server error' }); } } });
Image.create() validates the data against the schema BEFORE inserting. If name is missing → ValidationError with "Image name is required". If width is "banana" → type coercion fails. Bad data never enters the database.
04
Test Validation
InputExpected
No name field400: "Image name is required"
width: "banana"400: Cast to Number failed
format: "bmp"400: not a valid enum value
width: 0400: "Width must be at least 1"
All valid data201: Document created
05
Add a User Schema
const userSchema = new mongoose.Schema({ name: { type: String, required: true }, email: { type: String, required: true, unique: true, lowercase: true }, password: { type: String, required: true, minlength: 8 }, }, { timestamps: true });
Preparation for authentication (Session 60+). unique: true creates a database index that prevents duplicate emails. lowercase: true normalizes email casing automatically.
06
Close the Ticket
git switch -c feature/PIXELCRAFT-046-mongoose git add server/models/ server/routes/ git commit -m "Add Mongoose schemas with validation (PIXELCRAFT-046)" git push origin feature/PIXELCRAFT-046-mongoose # PR → Review → Merge → Close ticket ✅
CS.DEEP-DIVE

Data modeling is one of the most impactful architectural decisions.

How you structure data determines what queries are fast, what's impossible, and what breaks at scale.

// Two approaches:

Normalization
  Separate collections, reference by ID,
  join when needed. Saves space,
  harder to query.

Denormalization
  Embed related data in one document.
  Faster reads, harder to update.

MongoDB → favors denormalization
SQL     → favors normalization

// Neither is "better" — the choice
// depends on your access patterns.
"Schema Lab"
[A] Add a pre('save') hook to the Image schema that auto-generates a slug from the name: "My Photo.jpg" → "my-photo-jpg". Add a virtual getter for the full URL.
[B] Add a custom validator to the Image schema: fileSize must be less than 50MB (50 * 1024 * 1024). Test with a document that exceeds the limit.
[C] Compare Mongoose validation with Zod validation (Session 53). Both define valid data shapes. When do you use each? (Zod: API input, Mongoose: database layer. Validate at both boundaries.)
REF.MATERIAL
ARTICLE
Mongoose Team
Official schema guide: types, validators, defaults, virtuals, middleware hooks, and schema options. The definitive Mongoose reference.
MONGOOSEOFFICIALESSENTIAL
VIDEO
Web Dev Simplified
Practical Mongoose walkthrough: schemas, models, CRUD, validation, middleware, and population. Everything from this session in one video.
MONGOOSEPRACTICAL
ARTICLE
Mongoose Team
Complete validation reference: built-in validators, custom validators, async validation, and validation error handling.
VALIDATIONOFFICIAL
ARTICLE
MongoDB
Normalization vs denormalization, embedding vs referencing, and data modeling patterns for document databases. The theory behind schema design.
DATA MODELINGTHEORYOFFICIAL
VIDEO
MongoDB
Official schema design talk: embedding vs referencing, anti-patterns, and how to model data for real applications at scale.
SCHEMA DESIGNBEST PRACTICES
// LEAVE EXCITED BECAUSE
Invalid data can't enter the database. Schemas are guardrails. Your API rejects bad input with specific error messages. This is production-grade data integrity.