hoc-lap-trinh-16
  • Khởi tạo dự án: npm init -y
  • Cài đặt Express: npm i express
  • Cài đặt Vite Plugin Node: npm i -D vite-plugin-node
  • Thêm script start: vite trong package.json
{
...
   "scripts": {
    "start": "vite"
  },
...
}
  • Create file server.js
import express from "express";
import router from "./routes";
import connectMongoDB from "./config/dbconfig";
const app = express();
app.use(
  express.urlencoded({
    extended: true,
  })
);
app.use(express.json());
connectMongoDB("mongodb://127.0.0.1:27017/db_nodejs");
app.use("/", router);
export const viteNodeApp = app;

Create file routes/index.js

import { Router } from "express";
import booksRouter from "./books";
import authRouter from "./auth";
const router = Router();
router.get("/", (req, res) => {
  res.send("Home");
});
router.use("/books", booksRouter);
router.use("/auth", authRouter);

export default router;

Create file routes/books.js

import { Router } from "express";
import BooksController from "../controllers/books";
import { checkPermisson } from "../middlewares/checkPermisson";
const booksRouter = Router();
const booksController = new BooksController();

booksRouter.get("/", booksController.getAllBooks);
booksRouter.get("/:id", booksController.getBookDetail);
booksRouter.post("/", checkPermisson, booksController.createBook);
booksRouter.put("/:id", checkPermisson, booksController.updateBook);
booksRouter.delete("/:id", checkPermisson, booksController.deleteBook);

export default booksRouter;

Connect MongoDB

  • npm i mongoose
  • Create file: config/dbconfig.js
  • import connectMongoDB vào server.js
import mongoose from "mongoose";

export default async function connectMongoDB(dbUrl) {
  try {
    //mongodb://127.0.0.1:27017/db_name
    await mongoose.connect(dbUrl);
    console.log("Connect successfully!!!");
  } catch (error) {
    console.log("Connect failure!!!");
  }
}

Create file models/BookModel.js

Ref: https://mongoosejs.com/docs/schematypes.html

import mongoose from "mongoose";
const Schema = mongoose.Schema;

const BookSchema = new Schema(
  {
    title: { type: String, required: true },
    description: { type: String },
    author: { type: String },
    image: { type: String },
    price: { type: Number },
    rate: { type: Number },
  },
  { timestamps: true, versionKey: false }
);

const Book = mongoose.model("Book", BookSchema);

export default Book;

Create file controllers/books.js

Ref: https://mongoosejs.com/docs/queries.html

import Book from "../models/BookModel";
import { createValidator } from "../validations/book";

class BooksController {
  // GET /books
  async getAllBooks(req, res) {
    try {
      const books = await Book.find();
      res.status(200).json({
        message: "Get All Books Done",
        data: books,
      });
    } catch (error) {
      res.status(400).json({
        message: error.message,
      });
    }
  }
  // GET /books/:id
  async getBookDetail(req, res) {
    try {
      const book = await Book.findById(req.params.id);
      if (!book) {
        return res.status(404).json({
          message: " Book Not Found",
        });
      }
      res.status(200).json({
        message: "Get Book Detail Done",
        data: book,
      });
    } catch (error) {
      res.status(400).json({
        message: error.message,
      });
    }
  }
  // POST /books
  async createBook(req, res) {
    try {
      // validate
      const { error } = createValidator.validate(req.body, {
        abortEarly: false,
      });
      if (error) {
        const errors = error.details.map((err) => err.message);
        return res.status(400).json({
          message: errors,
        });
      }
      const book = await Book.create(req.body);
      res.status(200).json({
        message: "Create Book Done",
        data: book,
      });
    } catch (error) {
      res.status(400).json({
        message: error.message,
      });
    }
  }
  // PUT /books/:id
  async updateBook(req, res) {
    try {
      // validate
      const { error } = createValidator.validate(req.body, {
        abortEarly: false,
      });
      if (error) {
        const errors = error.details.map((err) => err.message);
        return res.status(400).json({
          message: errors,
        });
      }
      const book = await Book.findByIdAndUpdate(req.params.id, req.body, {
        new: true,
      });
      res.status(200).json({
        message: "Update Book Done",
        data: book,
      });
    } catch (error) {
      res.status(400).json({
        message: error.message,
      });
    }
  }
  // DELETE /books/:id
  async deleteBook(req, res) {
    try {
      const book = await Book.findByIdAndDelete(req.params.id);
      if (!book) {
        return res.status(404).json({
          message: "Book Not Found",
        });
      }
      res.status(200).json({
        message: "Delete Book Done",
      });
    } catch (error) {
      res.status(400).json({
        message: error.message,
      });
    }
  }
}
export default BooksController;

Authentication and Validation JOI

Create models/UserModel.js

// username, password, email, role: default: "member"
import mongoose from "mongoose";
const Schema = mongoose.Schema;

const UserSchema = new Schema(
  {
    username: { type: String, required: true },
    email: { type: String, required: true, unique: true },
    password: { type: String, required: true },
  },
  { timestamps: true, versionKey: false }
);

const User = mongoose.model("User", UserSchema);

export default User;

Create routes/auth.js

import { Router } from "express";
import AuthController from "../controllers/auth";

const authRouter = Router();

const authController = new AuthController();

authRouter.post("/register", authController.register);
authRouter.post("/login", authController.login);

export default authRouter;

Update routes/index.js

import { Router } from "express";
import booksRouter from "./books";
import authRouter from "./auth";

const router = Router();

router.get("/", (req, res) => {
  res.send("Home");
});

router.use("/auth", authRouter);
router.use("/books", booksRouter);

export default router;

Create validations/auth.js

import Joi from "joi";

const registerValidator = Joi.object({
  username: Joi.string(),
  email: Joi.string().required().email(),
  password: Joi.string().required(),
});

const loginValidator = Joi.object({
  email: Joi.string().required().email(),
  password: Joi.string().required(),
});

export { registerValidator, loginValidator };

Create controllers/auth.js

import User from "../models/UserModel";
import bcryptjs from "bcryptjs";
import { registerValidator, loginValidator } from "../validations/auth";
import jwt from "jsonwebtoken";
import dotenv from "dotenv";
dotenv.config();
class AuthController {
  // POST auth/register
  async register(req, res) {
    try {
      // validate
      const { error } = registerValidator.validate(req.body, {
        abortEarly: false,
      });
      if (error) {
        const errors = error.details.map((err) => err.message);
        return res.status(400).json({
          message: errors,
        });
      }
      const { username, email, password } = req.body;
      // check email co trong db chua findOne
      const emailExisted = await User.findOne({
        email,
      });
      if (emailExisted) {
        return res.status(400).json({
          message: "Email dc dung roi nhe !!!",
        });
      }
      // ma hoa password
      const hashPassword = await bcryptjs.hash(password, 10);
      const user = await User.create({
        username,
        email,
        password: hashPassword,
      });
      res.status(201).json({
        message: "register done",
        data: { ...user.toObject(), password: undefined },
      });
    } catch (error) {
      res.status(400).json({
        message: error.message,
      });
    }
  }

  // POST auth/login
  async login(req, res) {
    // validate
    const { error } = loginValidator.validate(req.body, {
      abortEarly: false,
    });
    if (error) {
      const errors = error.details.map((err) => err.message);
      return res.status(400).json({
        message: errors,
      });
    }
    // check email
    const { email, password } = req.body;
    const user = await User.findOne({
      email,
    });
    if (!user) {
      return res.status(401).json({
        message: "Tai khoan ko ton tai",
      });
    }
    // check password
    const checkPassword = await bcryptjs.compare(password, user.password);
    if (!checkPassword) {
      return res.status(401).json({
        message: "Tai khoan ko ton tai",
      });
    }
    // token
    const token = jwt.sign({ id: user._id }, process.env.SECRECT_KEY, {
      expiresIn: "1d",
    });
    // res token
    res
      .status(200)
      .json({
        message: "login done",
        data: { ...user.toObject(), password: undefined },
        token,
      });
  }
}

export default AuthController;

Create middlewares/authenticate.js

import jwt from "jsonwebtoken";
import User from "../models/UserModel";

const checkPermission = async (req, res, next) => {
  try {
    const token = req.headers.authorization?.split(" ")[1];
    if (!token) {
      return res.status(401).json({
        message: "No Authorization",
      });
    }
    // verify token
    const data = jwt.verify(token, process.env.SECRECT_KEY);
    if (!data) {
      return res.status(401).json({
        message: "No Authorization",
      });
    }
    // check user
    const user = await User.findById(data.id);
    if (!user) {
      return res.status(404).json({
        message: "Not Found",
      });
    }
    // user.role !== 'admin'
    // if(user.role !== 'admin'){
    //     return res.status(403).json({
    //         message:'Ban ko co quyen lam viec nay'
    //     })
    // }
    next();
  } catch (error) {
    res.status(400).json({
      message: error.message,
    });
  }
};

export { checkPermission };

By hoadv