nodejsnotes

Topic 011: Code snippets for controllers, routes, services, models and app.js/server.js in Node.js

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.

1. app.js / server.js

This 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}`);
});

2. Routes

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;

3. Controllers

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 });
  }
};

4. Services

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();
};

5. Models

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);

Flow Explanation

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

Conclusion

This architecture separates concerns effectively:

This separation makes the application modular, easier to maintain, and scalable.


dbconfig.js

require("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.js

const 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.js

const 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.js

const mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
  name: String,
  email: String,
  age: Number,
});

const userModel = mongoose.model("users", userSchema);

module.exports = userModel;

usersRoutes.js

const 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.js

const 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.js

const 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}`);
});