We'll create detailed schemas and models for the products of the cosmetics warehouse. This will CRUD data records in our database!
Lab guide for designing schemas and models for our cosmetics warehouse using Mongoose.
The guide includes:
Step-by-step instructions for creating each model (Category, Supplier, Product, and Inventory) Detailed Mongoose schemas with field validations, virtual fields, and middleware Explanations of key concepts like references, virtuals, and middleware An updated app.js file that incorporates all the new models A challenge to create a new product document Reflection questions to deepen understanding of schema design Key features:
Use of Mongoose schema options like timestamps and virtuals Implementation of data validation rules Use of references to create relationships between models (PREDICATE JOINS) Pre-save and pre-find middleware for automated slug creation and population Virtual fields for derived data (like subcategories and low stock status)
Lab 2: Bringing Your Data to Life with Mongoose Schemas and Models
## Objective
In this lab, you'll create the backbone of our cosmetics warehouse database by designing and implementing Mongoose schemas and models for:
Products
Categories
Suppliers
Inventory.
Here we see that MONGOOSE SCHEMA are like SQL Tables in that they structure the documents / rowsets.
## Prerequisites
- Completed Lab 1: Setting up the environment
- Node.js and npm installed
- MongoDB Atlas connection established
### Step 1: Set Up Your Models Directory
First, let's create a new directory to house our models:
mkdir models
cd models
```
Step 2: Create the Category Schema and Model
Let's start with categories. Create a file named `category.js`:
```javascript
const mongoose = require('mongoose');
const categorySchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'A category must have a name'],
unique: true,
trim: true,
maxlength: [40, 'A category name must have less or equal than 40 characters'],
minlength: [3, 'A category name must have more or equal than 3 characters']
},
description: {
type: String,
trim: true
},
slug: String,
parent: {
type: mongoose.Schema.ObjectId,
ref: 'Category',
default: null
}
}, {
timestamps: true,
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
// Virtual populate
categorySchema.virtual('subcategories', {
ref: 'Category',
foreignField: 'parent',
localField: '_id'
});
const Category = mongoose.model('Category', categorySchema);
module.exports = Category;
```
Step 3: Create the Supplier Schema and Model
Now, let's create our suppliers. Create a file named `supplier.js`:
```javascript
const mongoose = require('mongoose');
const validator = require('validator');
const supplierSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'A supplier must have a name'],
trim: true,
maxlength: [50, 'A supplier name must have less or equal than 50 characters']
},
email: {
type: String,
required: [true, 'A supplier must have an email'],
unique: true,
lowercase: true,
validate: [validator.isEmail, 'Please provide a valid email']
},
phone: {
type: String,
required: [true, 'A supplier must have a phone number']
},
address: {
street: String,
city: String,
state: String,
zipCode: String,
country: String
},
active: {
type: Boolean,
default: true,
select: false
}
}, {
timestamps: true,
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
const Supplier = mongoose.model('Supplier', supplierSchema);
module.exports = Supplier;
```
Step 4: Create the Product Schema and Model
Time for the star of our show - products! Create a file named `product.js`:
```javascript
const mongoose = require('mongoose');
const slugify = require('slugify');
const productSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'A product must have a name'],
unique: true,
trim: true,
maxlength: [100, 'A product name must have less or equal than 100 characters'],
minlength: [3, 'A product name must have more or equal than 3 characters']
},
slug: String,
description: {
type: String,
required: [true, 'A product must have a description']
},
price: {
type: Number,
required: [true, 'A product must have a price']
},
category: {
type: mongoose.Schema.ObjectId,
ref: 'Category',
required: [true, 'Product must belong to a category']
},
supplier: {
type: mongoose.Schema.ObjectId,
ref: 'Supplier',
required: [true, 'Product must have a supplier']
},
imageCover: {
type: String,
required: [true, 'A product must have a cover image']
},
images: [String],
createdAt: {
type: Date,
default: Date.now(),
select: false
},
sku: {
type: String,
required: [true, 'A product must have a SKU'],
unique: true
},
inStock: {
type: Boolean,
default: true
}
}, {
timestamps: true,
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
// DOCUMENT MIDDLEWARE: runs before .save() and .create()
productSchema.pre('save', function(next) {
this.slug = slugify(this.name, { lower: true });
next();
});
// QUERY MIDDLEWARE
productSchema.pre(/^find/, function(next) {
this.populate({
path: 'category',
select: 'name'
}).populate({
path: 'supplier',
select: 'name'
});
next();
});
const Product = mongoose.model('Product', productSchema);
module.exports = Product;
```
Step 5: Create the Inventory Schema and Model
Last but not least, let's manage our inventory. Create a file named `inventory.js`:
```javascript
const mongoose = require('mongoose');
const inventorySchema = new mongoose.Schema({
product: {
type: mongoose.Schema.ObjectId,
ref: 'Product',
required: [true, 'Inventory must belong to a product']
},
quantity: {
type: Number,
required: [true, 'Inventory must have a quantity'],
min: [0, 'Quantity cannot be negative']
},
location: {
type: String,
required: [true, 'Inventory must have a storage location']
},
lastRestocked: {
type: Date,
default: Date.now()
},
lowStockThreshold: {
type: Number,
default: 10
}
}, {
timestamps: true,
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
inventorySchema.virtual('isLowStock').get(function() {
return this.quantity <= this.lowStockThreshold;
});
inventorySchema.pre(/^find/, function(next) {
this.populate({
path: 'product',
select: 'name price'
});
next();
});
const Inventory = mongoose.model('Inventory', inventorySchema);
module.exports = Inventory;
```
Step 6: Bring It All Together
Now, let's update our `app.js` to include these models:
```javascript
const express = require('express');
const mongoose = require('mongoose');
const config = require('./config');
// Import models
const Category = require('./models/category');
const Supplier = require('./models/supplier');
const Product = require('./models/product');
const Inventory = require('./models/inventory');
const app = express();
const port = 3000;
mongoose.connect(config.mongoURI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('MongoDB connected... Let\'s build something awesome! 🚀'))
.catch(err => console.log(err));
app.get('/', (req, res) => {
res.send('Welcome to Cosmetics Warehouse API - Your data is now alive! 💄✨');
});
app.listen(port, () => {
console.log(`Server is up and running at http://localhost:${port} - Time to make your database beautiful!`);
});
```
## Awesome Job!
You've just created a robust data structure for our cosmetics warehouse! 🎉 These schemas and models will allow us to:
- Organize products into categories and subcategories
- Keep track of our suppliers
- Manage product details including price, description, and images
- Monitor inventory levels and locations
In the next lab, we'll start building API endpoints to interact with this data. Get ready to bring your warehouse to life!
## Challenge
Try to create a new product document and save it to the database. How would you ensure that the referenced category and supplier exist before saving the product?
## Reflection
1. How might this schema design change if we wanted to implement product variants (e.g., different sizes or colors of the same product)?
2. What benefits do the virtual fields (like `subcategories` in Category and `isLowStock` in Inventory) provide?
3. How could we optimize these schemas for faster queries as our database grows?
Keep up the great work! You're well on your way to becoming a MongoDB maestro! 🎵📊