Theory
Give the backend a shape
FestConnect now saves real data: users, events, registrations. As soon as data and logic grow, a one-file server becomes a mess. A well-structured backend separates two concerns: what the data looks like (models) and what happens on each route (controllers).
For the database, FestConnect uses MongoDB (which you met in Sem 5) through a library called Mongoose, which defines data shapes and gives clean create-read-update-delete methods. This lesson covers models, controllers, and CRUD with Mongoose, so the backend stays organised as it grows.
Theory
Models with Mongoose
Mongoose is an ODM (Object Data Modelling) library for MongoDB in Node. You describe your data with a Schema, the fields and their types, and compile it into a Model. The model is your gateway to that collection: you use it to create and query documents.
MongoDB stores documents (JSON-like objects) in collections (like tables of flexible rows). A Mongoose schema adds structure and validation on top of MongoDB's flexibility, so an Event always has a name and date of the right types. Defining a clear model per entity (User, Event, Registration) is the foundation of an organised backend.
Practical
A Mongoose model
const mongoose = require('mongoose');
const eventSchema = new mongoose.Schema({
name: { type: String, required: true },
date: { type: Date, required: true },
seats: { type: Number, default: 50 },
});
// Compile the schema into a model (the 'events' collection):
const Event = mongoose.model('Event', eventSchema);
module.exports = Event;This example runs in Gri-Learn on the web, where you can edit it and see the output.
Theory
CRUD, and controllers
The model gives you clean CRUD methods, which return Promises (use async/await):
- Create:
Event.create({...})(ornew Event({...}).save()). - Read:
Event.find()(all) orEvent.findById(id)(one). - Update:
Event.findByIdAndUpdate(id, {...}). - Delete:
Event.findByIdAndDelete(id).
The controller is where you call these in response to a route. Keeping controllers in their own files (and models in theirs) keeps your route definitions thin, a route just says 'POST /api/events goes to createEvent', and the controller holds the actual logic. Clear separation, easy to grow.
Practical
A controller using the model (CRUD)
const Event = require('../models/event');
// create
exports.createEvent = async (req, res) => {
const event = await Event.create(req.body);
res.status(201).json(event);
};
// read all
exports.getEvents = async (req, res) => {
const events = await Event.find();
res.json(events);
};
// delete by id
exports.deleteEvent = async (req, res) => {
await Event.findByIdAndDelete(req.params.id);
res.status(204).send();
};This example runs in Gri-Learn on the web, where you can edit it and see the output.
Quiz
In a Mongoose-based backend, what is the role of a Schema?
- It starts the Express server
- It defines the shape of the data (fields and their types) for a MongoDB collection, and is compiled into a Model used for CRUD
- It handles CORS
- It is the same as a route
Show the answer
It defines the shape of the data (fields and their types) for a MongoDB collection, and is compiled into a Model used for CRUD
A Mongoose Schema defines the structure of your data, the fields and their types (and validation), for a MongoDB collection, and you compile it into a Model that provides the CRUD methods (create, find, update, delete). Option A is wrong: starting the server is app.listen in Express, unrelated to schemas. Option C is wrong: CORS is handled by middleware, not a schema. Option D confuses layers: a route maps a URL and method to a handler, whereas a schema describes DATA; they live in different parts of the app (routes/controllers vs models). Schema describes the data shape, Model gives the operations.
Think first
Why add Mongoose schemas when MongoDB is schema-less?
MongoDB lets you store documents of any shape. So why impose a Mongoose schema on top? Then tap.
Show the answer
Because MongoDB's flexibility is powerful but dangerous without discipline, and a Mongoose schema gives you STRUCTURE, VALIDATION, and CLARITY while keeping the benefits. MongoDB itself will happily store documents of wildly different shapes in the same collection, one event with a name and date, another missing the date, a third with 'date' stored as a string instead of a real date. That freedom quickly leads to messy, inconsistent data that your code cannot rely on, causing bugs when you later assume every event has a proper date. A Mongoose schema declares your intended shape: name is a required String, date is a required Date, seats is a Number defaulting to 50. Now Mongoose VALIDATES incoming data against that shape, rejecting or flagging documents that do not fit, so your collection stays consistent and your code can trust it. The schema also DOCUMENTS the data model for anyone reading the code (you can see at a glance what an Event is), provides sensible defaults and type conversion, and enables features like hooks and relationships. Crucially, you still keep MongoDB's advantages, you can evolve the schema over time, and MongoDB remains fast and JSON-friendly underneath. So Mongoose is a deliberate, opt-in layer of order on top of MongoDB's flexibility: freedom where you want it, guarantees where you need them. Structure by choice beats chaos by default.
Summary
Key takeaways
- Organise the backend by separating models (data shapes) from controllers (route logic).
- Mongoose is an ODM for MongoDB in Node: define a Schema (fields and types), compile it into a Model.
- MongoDB stores JSON-like documents in collections; a schema adds structure and validation.
- CRUD with the model: create (create/save), read (find/findById), update (findByIdAndUpdate), delete (findByIdAndDelete); these return Promises (use async/await).
- Controllers call the model in response to routes; keeping them in separate files keeps routes thin.
- Mongoose schemas add validation and consistency on top of MongoDB's flexibility.
- Memory hook: models shape the data, controllers hold the logic, Mongoose gives CRUD.