In the past lessons, we learned how to set up MongoDB with Express.js, define Mongoose schemas and models, and manage ToDo
items. Today, we'll take a step further and learn about creating relationships between models. Models in MongoDB help us structure our data, but sometimes we need to connect different types of data, like linking todo items to their categories. Creating these connections is crucial for building more complex applications, enabling more detailed queries, such as finding all tasks in a specific category or aggregating tasks by categories in a task management app.
In this lesson, you'll learn:
- What relationships between models are
- How to create and use references between models
- How to populate data from related models
First, we need to connect to MongoDB. We've done this multiple times in previous lessons, so here's the code snippet for reference:
JavaScript1const mongoose = require('mongoose'); 2const express = require('express'); 3 4const app = express(); 5const PORT = 3000; 6 7// Set mongoose strictQuery to true to suppress deprecation warning 8mongoose.set('strictQuery', true); 9 10// Connect to MongoDB, ensure your MongoDB server is running 11mongoose.connect('mongodb://127.0.0.1:27017/todo-app', { 12 useNewUrlParser: true, 13 useUnifiedTopology: true, 14}) 15.then(() => console.log("Connected to MongoDB")) 16.catch((error) => console.error("Failed to connect to MongoDB:", error)); 17 18app.use(express.json());
In this code, we set up the Express application, and connect to our MongoDB database.
Next, we'll define the schemas for our Category
and ToDo
models.
JavaScript1// Define schema for categories 2const categorySchema = new mongoose.Schema({ 3 name: { 4 type: String, 5 required: true, 6 trim: true, 7 minLength: 3, 8 maxLength: 50 9 } 10}); 11 12// Create Category model 13const Category = mongoose.model('Category', categorySchema);
In the above schema definition, trim
option removes any whitespace from the input. minLength
and maxLength
ensure the category name stays within the specified length range (3 to 50 characters).
Now, we'll create the ToDo
model with a reference to the Category
model.
JavaScript1const todoSchema = new mongoose.Schema({ 2 task: { type: String, required: true }, 3 category: { type: mongoose.Schema.Types.ObjectId, ref: 'Category' } 4}); 5 6// Create ToDo model 7const Todo = mongoose.model('Todo', todoSchema);
In this step, the category
field in our ToDo
schema is of type ObjectId
and references the Category
model, allowing us to link ToDo
items with specific categories.
Now, we’ll set up routes to handle CRUD operations for both Category
and ToDo
models.
Routes for Categories:
JavaScript1// Routes for categories 2app.get('/categories', async (req, res) => { 3 const categories = await Category.find(); 4 res.send(categories); 5}); 6 7app.post('/categories', async (req, res) => { 8 const category = new Category(req.body); 9 await category.save(); 10 res.send(category); 11});
These routes allow us to get a list of categories and create new categories.
Routes for ToDo Items:
JavaScript1// Routes for todo items 2app.get('/todos', async (req, res) => { 3 const todos = await Todo.find().populate('category'); 4 res.send(todos); 5}); 6 7app.post('/todos', async (req, res) => { 8 const todo = new Todo(req.body); 9 await todo.save(); 10 res.send(todo); 11});
These routes allow us to:
- Retrieve a list of todo items, with populated category data, to provide more detailed and comprehensive information.
- Create new todo items, including their category references, to ensure each task is associated with the appropriate category.
The .populate('category')
method converts the ObjectId
in the category
field into the actual Category
document it references, providing richer query results by including full category details.
To handle errors gracefully, we add an error handling middleware.
JavaScript1// Error handling middleware 2app.use((err, req, res, next) => { 3 console.error(err.stack); 4 res.status(500).send('Something broke!'); 5});
Finally, let's start the Express server.
JavaScript1// Start Express server 2app.listen(PORT, () => { 3 console.log(`Server listening on port ${PORT}`); 4});
In this lesson, we learned how to create relationships between models in Mongoose
. To recap, we began by understanding references and their importance, created Category
and ToDo
models, added references to link them, updated our routes to handle category-linked tasks, and used the populate
method to fetch comprehensive data. These skills are essential for building complex applications where different types of data need to be connected.
Now, it's time to practice these skills with some exercises. These exercises will help reinforce your understanding and make you more confident in working with relationships between models. Happy coding!