Now we’re tying together CRUD operations with models, which is the backbone of most Node.js backend apps, whether you use MongoDB/Mongoose or PostgreSQL/Sequelize. I’ll break it down step by step with a generic pattern you can adapt to either SQL or NoSQL.
1️⃣ Folder Structure Recap
src/
├── routes/
│ └── userRoutes.js
├── controllers/
│ └── userController.js
├── models/
│ └── User.js
├── app.js
└── server.js
-
models/
→ define your data structure -
controllers/
→ implement CRUD logic -
routes/
→ expose endpoints
2️⃣ CRUD Operations
Create (POST)
- Purpose: Add a new record to the database
// Controller: createUser
const createUser = async (req, res) => {
try {
const user = await User.create(req.body); // MongoDB/Mongoose
// const user = await User.create(req.body); // Sequelize
res.status(201).json(user);
} catch (err) {
res.status(400).json({ error: err.message });
}
};
Read (GET)
- Purpose: Fetch data from the database
// Controller: getUsers
const getUsers = async (req, res) => {
try {
const users = await User.find(); // Mongoose
// const users = await User.findAll(); // Sequelize
res.json(users);
} catch (err) {
res.status(500).json({ error: err.message });
}
};
// Controller: getUserById
const getUserById = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findById(id); // Mongoose
// const user = await User.findByPk(id); // Sequelize
if (!user) return res.status(404).json({ error: "User not found" });
res.json(user);
} catch (err) {
res.status(500).json({ error: err.message });
}
};
Update (PUT/PATCH)
- Purpose: Modify an existing record
const updateUser = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findByIdAndUpdate(id, req.body, { new: true }); // Mongoose
// const user = await User.update(req.body, { where: { id }, returning: true }); // Sequelize
if (!user) return res.status(404).json({ error: "User not found" });
res.json(user);
} catch (err) {
res.status(400).json({ error: err.message });
}
};
Delete (DELETE)
- Purpose: Remove a record
const deleteUser = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findByIdAndDelete(id); // Mongoose
// const deleted = await User.destroy({ where: { id } }); // Sequelize
if (!user) return res.status(404).json({ error: "User not found" });
res.status(204).send(); // 204 = no content
} catch (err) {
res.status(500).json({ error: err.message });
}
};
3️⃣ Routes Integration
const express = require("express");
const router = express.Router();
const userController = require("../controllers/userController");
router.get("/", userController.getUsers);
router.get("/:id", userController.getUserById);
router.post("/", userController.createUser);
router.put("/:id", userController.updateUser);
router.delete("/:id", userController.deleteUser);
module.exports = router;
4️⃣ Model Integration Example
Mongoose Model (User.js
)
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
age: Number
});
module.exports = mongoose.model("User", userSchema);
Sequelize Model (User.js
)
const { DataTypes } = require("sequelize");
const sequelize = require("../config/db");
const User = sequelize.define("User", {
name: { type: DataTypes.STRING, allowNull: false },
email: { type: DataTypes.STRING, allowNull: false, unique: true },
age: DataTypes.INTEGER
});
module.exports = User;
5️⃣ Error Handling & Response Patterns
-
Use try/catch for async errors
-
Return consistent JSON format:
{
"success": true,
"data": {...},
"error": null
}
- For errors:
{
"success": false,
"data": null,
"error": "User not found"
}
✅ Summary
-
CRUD = Create, Read, Update, Delete
-
Controllers = implement CRUD logic using models
-
Routes = expose endpoints for clients
-
Models = define structure & validation of data (Mongoose or Sequelize)
-
Error handling = try/catch + consistent JSON response