Topic 011: Code snippets for controllers, routes, services, models and app.js/server.js in Node.js
Sure! I’ll walk you through a basic Node.js application using Express, illustrating the flow of data through controllers, routes, services, and models, along with the setup in app.js or server.js.
app.js / server.jsThis file is the entry point of the application. It sets up the server and integrates all the routes.
// app.js or server.js
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const port = 3000;
// Middleware
app.use(bodyParser.json());
// Routes
const userRoutes = require("./routes/userRoutes");
app.use("/users", userRoutes);
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
Routes define the endpoints of your application and map them to the corresponding controller functions.
// routes/userRoutes.js
const express = require("express");
const router = express.Router();
const userController = require("../controllers/userController");
router.get("/", userController.getAllUsers);
router.get("/:id", userController.getUserById);
router.post("/", userController.createUser);
module.exports = router;
Controllers handle the incoming requests, process them, and return responses. They often call services to perform business logic.
// controllers/userController.js
const userService = require("../services/userService");
exports.getAllUsers = async (req, res) => {
try {
const users = await userService.getAllUsers();
res.json(users);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
exports.getUserById = async (req, res) => {
try {
const user = await userService.getUserById(req.params.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
res.json(user);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
exports.createUser = async (req, res) => {
try {
const user = await userService.createUser(req.body);
res.status(201).json(user);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
Services contain the business logic of the application. They interact with the models to perform database operations.
// services/userService.js
const User = require("../models/userModel");
exports.getAllUsers = async () => {
return await User.find();
};
exports.getUserById = async (id) => {
return await User.findById(id);
};
exports.createUser = async (userData) => {
const user = new User(userData);
return await user.save();
};
Models define the schema of your data and handle interactions with the database. In this example, we’re using Mongoose for MongoDB.
// models/userModel.js
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
});
module.exports = mongoose.model("User", userSchema);
Server Setup (app.js / server.js): The application starts by running the app.js file. This sets up the Express server and integrates middleware like body-parser to handle JSON requests. It then imports and uses the user routes.
Routing (routes/userRoutes.js): When a request hits an endpoint (e.g., /users), it is directed to the corresponding route handler defined in userRoutes.js.
Controller (controllers/userController.js): The route handler invokes the appropriate controller function (e.g., getAllUsers). The controller’s responsibility is to handle the request and response objects. It may call a service function to perform the business logic.
Service (services/userService.js): The service function performs the actual business logic. For example, getAllUsers might retrieve all users from the database by calling methods on the user model.
Model (models/userModel.js): The model defines the structure of the data and interacts with the database. For instance, in userModel.js, Mongoose is used to define the schema and provide methods like find and findById.
This architecture separates concerns effectively:
This separation makes the application modular, easier to maintain, and scalable.
dbconfig.jsrequire("dotenv").config();
const mongoose = require("mongoose");
//ES7 method to connect with DB
const dbConnect = async () => {
try {
//const uri = process.env.MONGODB_URI;
const uri = "mongodb://localhost:27017/MERN_CRUD";
const options = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
const conn = await mongoose.connect(uri, options);
console.log(`MongoDB database Connected : ${conn.connection.host}`);
} catch (error) {
console.log(`Error connecting to MongoDB : ${error}`);
}
};
module.exports = dbConnect;
/*
ES6 method to connect with DB
module.exports = () => {
const connection = mongoose
.connect(process.env.MONGODB_URI)
.then((result) => console.log(`MongoDB databse Connected : ${connect.connection.host}`))
.catch((error) => console.log(`Error connecting to MongoDB : ${error}`));
};
*/
userControllers.jsconst express = require("express");
//users model
const Users = require("../model/usersModel");
// @route POST /api/users/
// @desc Create a user
// @access Public
exports.createUser = async (req, res) => {
try {
const newUser = new Users(req.body);
await newUser.save();
res.status(201).json(newUser);
} catch (error) {
res.status(400).json({ message: error.message });
}
};
// @route GET /api/users/
// @desc Get all users
// @access Public
exports.getAllUsers = async (req, res) => {
try {
const users = await Users.find();
res.json(users);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
// @route GET /api/users/:id
// @desc Get a specific user
// @access Public
exports.getUserById = async (req, res) => {
try {
const user = await Users.findById(req.params.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
res.json(user);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
// @route PUT /api/users/:id
// @desc Update a user
// @access Public
exports.updateUser = async (req, res) => {
try {
const updatedUser = await Users.findByIdAndUpdate(req.params.id, req.body, { new: true });
if (!updatedUser) {
return res.status(404).json({ message: "User not found" });
}
res.json(updatedUser);
} catch (error) {
res.status(400).json({ message: error.message });
}
};
// @route DELETE /api/users/:id
// @desc Delete a user
// @access Public
exports.deleteUser = async (req, res) => {
try {
const deletedUser = await Users.findByIdAndDelete(req.params.id);
if (!deletedUser) {
return res.status(404).json({ message: "User not found" });
}
res.json({ message: "User deleted successfully" });
} catch (error) {
res.status(500).json({ message: error.message });
}
};
defaultDB.jsconst mongoose = require("mongoose");
const dbConnect = require("../config/dbconfig");
const userModel = require("../model/usersModel");
const initializeDB = async () => {
// Connect to the database
dbConnect();
// Check if there are any existing users
const existingUsers = await userModel.find();
// If no users exist, seed the database with default data
if (existingUsers.length === 0) {
try {
// Add three default users
const defaultUsers = [
{ name: "Alex", email: "alex@example.com", age: 30 },
{ name: "Mike", email: "mike@example.com", age: 25 },
{ name: "John", email: "johne@example.com", age: 35 },
];
// Insert the default users into the database
await userModel.insertMany(defaultUsers);
console.log("Default data seeded successfully");
} catch (error) {
console.error(`Error seeding default data: ${error.message}`);
}
}
};
module.exports = initializeDB;
usersModel.jsconst mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
name: String,
email: String,
age: Number,
});
const userModel = mongoose.model("users", userSchema);
module.exports = userModel;
usersRoutes.jsconst express = require("express");
const router = express.Router();
const usersController = require("../controllers/usersControllers");
// POST /api/users - Create a new user
router.post("/", usersController.createUser);
// GET /api/users - Get all users
router.get("/", usersController.getAllUsers);
// GET /api/users/:id - Get a single user by ID
router.get("/:id", usersController.getUserById);
// PUT /api/users/:id - Update a user by ID
router.put("/:id", usersController.updateUser);
// DELETE /api/users/:id - Delete a user by ID
router.delete("/:id", usersController.deleteUser);
module.exports = router;
app.jsconst express = require("express");
const path = require("path");
const dbConnect = require("./config/dbconfig");
const app = express();
// database connection
dbConnect();
// Use hpp, xss, helmet, cors express-mogo-sanitize for better security
// Use serve-index to serves pages that contain directory listings for a given path.
// middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use((req, res, next) => {
res.locals.path = req.path;
next();
});
// routes
app.use("/api/users", require("./routes/usersRoutes"));
module.exports = app;
/* Production
if (process.env.NODE_ENV === "production") {
// Set static folder
app.use(express.static("client/build"));
app.get("*", (req, res) => {
res.sendfile(path.resolve(__dirname, "client", "build", "index.html"));
});
}*/
server.jsconst app = require("./app");
require("dotenv").config();
const initializeDB = require("./model/defaultDB");
// Initialize default database connection
initializeDB();
const PORT = process.env.PORT || 5000;
//making app to listen on port from .env file which is 8001
app.listen(PORT, () => {
console.log(`App is running on port ${PORT}`);
});