├── backend ├── middleware │ ├── catchAsyncErrors.js │ ├── auth.js │ └── error.js ├── config │ ├── config.env │ └── database.js ├── utils │ ├── errorhander.js │ ├── jwtToken.js │ ├── sendEmail.js │ └── apifeatures.js ├── app.js ├── routes │ ├── userRoute.js │ └── productRoute.js ├── server.js ├── models │ ├── productModel.js │ └── userModel.js └── controllers │ ├── productController.js │ └── userController.js └── README.md /backend/middleware/catchAsyncErrors.js: -------------------------------------------------------------------------------- 1 | module.exports = (theFunc) => (req, res, next) => { 2 | Promise.resolve(theFunc(req, res, next)).catch(next); 3 | }; 4 | -------------------------------------------------------------------------------- /backend/config/config.env: -------------------------------------------------------------------------------- 1 | PORT = 2 | 3 | DB_URI = 4 | 5 | JWT_SECRET = 6 | 7 | JWT_EXPIRE = 8 | 9 | COOKIE_EXPIRE = 10 | 11 | SMPT_SERVICE = 12 | 13 | SMPT_MAIL = 14 | 15 | SMPT_PASSWORD = 16 | 17 | SMPT_HOST = 18 | 19 | SMPT_PORT = -------------------------------------------------------------------------------- /backend/utils/errorhander.js: -------------------------------------------------------------------------------- 1 | class ErrorHander extends Error{ 2 | constructor(message,statusCode){ 3 | super(message); 4 | this.statusCode = statusCode 5 | 6 | Error.captureStackTrace(this,this.constructor); 7 | } 8 | } 9 | 10 | module.exports = ErrorHander -------------------------------------------------------------------------------- /backend/config/database.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const connectDatabase = () => { 4 | mongoose 5 | .connect(process.env.DB_URI, { 6 | useNewUrlParser: true, 7 | useUnifiedTopology: true, 8 | // useCreateIndex: true, 9 | }) 10 | .then((data) => { 11 | console.log(`Mongodb connected with server: ${data.connection.host}`); 12 | }); 13 | }; 14 | 15 | module.exports = connectDatabase; 16 | -------------------------------------------------------------------------------- /backend/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const cookieParser = require("cookie-parser"); 4 | const errorMiddleware = require("./middleware/error"); 5 | 6 | app.use(express.json()); 7 | app.use(cookieParser()); 8 | 9 | // Route ko import ker legay 10 | const product = require("./routes/productRoute"); 11 | const user = require("./routes/userRoute"); 12 | 13 | app.use("/api/v1", product); 14 | app.use("/api/v1", user); 15 | 16 | // Middleware for Error 17 | app.use(errorMiddleware); 18 | 19 | module.exports = app; 20 | -------------------------------------------------------------------------------- /backend/routes/userRoute.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { 3 | registerUser, 4 | loginUser, 5 | logout, 6 | forgotPassword, 7 | resetPassword, 8 | } = require("../controllers/userController"); 9 | const router = express.Router(); 10 | 11 | router.route("/register").post(registerUser); 12 | 13 | router.route("/login").post(loginUser); 14 | 15 | router.route("/password/forgot").post(forgotPassword); 16 | 17 | router.route("/password/reset/:token").put(resetPassword); 18 | 19 | router.route("/logout").get(logout); 20 | 21 | module.exports = router; 22 | -------------------------------------------------------------------------------- /backend/utils/jwtToken.js: -------------------------------------------------------------------------------- 1 | const sendToken = (user, statusCode, res) => { 2 | const token = user.getJWTToken(); 3 | 4 | // options for cookie ( hame cookie me bhajna ho to uske liye option hote h ) 5 | const options = { 6 | expires: new Date( 7 | Date.now() + process.env.COOKIE_EXPIRE * 24 * 60 * 60 * 1000 8 | ), 9 | httpOnly: true, 10 | }; 11 | 12 | res.status(statusCode).cookie("token", token, options).json({ 13 | success: true, 14 | user, 15 | token, 16 | }); 17 | }; 18 | 19 | module.exports = sendToken; 20 | -------------------------------------------------------------------------------- /backend/utils/sendEmail.js: -------------------------------------------------------------------------------- 1 | const nodeMailer = require("nodemailer"); 2 | 3 | const sendEmail = async (options) => { 4 | const transporter = nodeMailer.createTransport({ 5 | host: process.env.SMPT_HOST, 6 | port: process.env.SMPT_PORT, 7 | service: process.env.SMPT_SERVICE, 8 | auth: { 9 | user: process.env.SMPT_MAIL, 10 | pass: process.env.SMPT_PASSWORD, 11 | }, 12 | }); 13 | 14 | const mailOptions = { 15 | from: process.env.SMPT_MAIL, 16 | to: options.email, 17 | subject: options.subject, 18 | text: options.message, 19 | }; 20 | 21 | await transporter.sendMail(mailOptions); 22 | }; 23 | 24 | module.exports = sendEmail; 25 | -------------------------------------------------------------------------------- /backend/routes/productRoute.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { 3 | getAllProducts, 4 | createProduct, 5 | updateProduct, 6 | deleteProduct, 7 | getProductDetails, 8 | } = require("../controllers/productController"); 9 | const { isAuthenticatedUser, authorizeRoles } = require("../middleware/auth"); 10 | 11 | const router = express.Router(); 12 | 13 | router.route("/products").get(getAllProducts); 14 | 15 | router 16 | .route("/product/new") 17 | .post(isAuthenticatedUser, authorizeRoles("admin"), createProduct); 18 | 19 | router 20 | .route("/product/:id") 21 | .put(isAuthenticatedUser, authorizeRoles("admin"), updateProduct) 22 | .delete(isAuthenticatedUser, authorizeRoles("admin"), deleteProduct) 23 | .get(getProductDetails); 24 | 25 | module.exports = router; 26 | -------------------------------------------------------------------------------- /backend/server.js: -------------------------------------------------------------------------------- 1 | const app = require("./app"); 2 | const dotenv = require("dotenv"); 3 | const connectDatabase = require("./config/database"); 4 | 5 | // Handling UnCaught Exception 6 | process.on("uncaughtException", (err) => { 7 | console.log(`Error ${err.message}`); 8 | console.log(`Shutting down the server due to Uncaught Exception`); 9 | process.exit(1); 10 | }); 11 | 12 | // config 13 | dotenv.config({ path: "backend/config/config.env" }); 14 | 15 | // connecting to database 16 | connectDatabase(); 17 | 18 | const server = app.listen(process.env.PORT, () => { 19 | console.log(`server is working on http://localhost:${process.env.PORT}`); 20 | }); 21 | 22 | // unhandled Promise Rejection 23 | process.on("unhandledRejection", (err) => { 24 | console.log(`Error: ${err.message}`); 25 | console.log(`Shutting down the server due to Unhandled Promise Rejection`); 26 | 27 | server.close(() => { 28 | process.exit(1); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /backend/middleware/auth.js: -------------------------------------------------------------------------------- 1 | const ErrorHander = require("../utils/errorhander"); 2 | const catchAsyncErrors = require("./catchAsyncErrors"); 3 | const jwt = require("jsonwebtoken"); 4 | const User = require("../models/userModel"); 5 | 6 | exports.isAuthenticatedUser = catchAsyncErrors(async (req, res, next) => { 7 | const { token } = req.cookies; 8 | 9 | if (!token) { 10 | return next(new ErrorHander("Please Login to access this resource", 401)); 11 | } 12 | 13 | const decodedData = jwt.verify(token, process.env.JWT_SECRET); 14 | req.user = await User.findById(decodedData.id); 15 | 16 | next(); 17 | }); 18 | 19 | exports.authorizeRoles = (...roles) => { 20 | return (req, res, next) => { 21 | if (!roles.includes(req.user.role)) { 22 | return next( 23 | new ErrorHander( 24 | `Role: ${req.user.role} is not allowed to access this resouce`, 25 | 403 26 | ) 27 | ); 28 | } 29 | 30 | next(); 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /backend/middleware/error.js: -------------------------------------------------------------------------------- 1 | const ErrorHander = require("../utils/errorhander"); 2 | 3 | module.exports = (err, req, res, next) => { 4 | err.statusCode = err.statusCode || 500; 5 | err.message = err.message || "Internal Server Error"; 6 | 7 | // Wrong Mongodb Id error 8 | if (err.name === "CastError") { 9 | const message = `Resource not found. Invalid: ${err.path}`; 10 | err = new ErrorHander(message, 400); 11 | } 12 | 13 | // Mongoose duplicate key error 14 | if (err.code === 11000) { 15 | const message = `Duplicate ${object.keys(err.keyword)} Entered`; 16 | err = new ErrorHander(message, 400); 17 | } 18 | 19 | // Wrong JWT error 20 | if (err.name === "JsonWebTokenError") { 21 | const message = `Json Web Token is Invalid, Try Again`; 22 | err = new ErrorHander(message, 400); 23 | } 24 | 25 | // JWT EXPIRE Error 26 | if (err.name === "TokenExpireError") { 27 | const message = `Json Web Token is Expire, Try Again`; 28 | err = new ErrorHander(message, 400); 29 | } 30 | 31 | res.status(err.statusCode).json({ 32 | success: false, 33 | message: err.message, 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /backend/utils/apifeatures.js: -------------------------------------------------------------------------------- 1 | class ApiFeatures { 2 | constructor(query, queryStr) { 3 | this.query = query; 4 | this.queryStr = queryStr; 5 | } 6 | 7 | // search nam ka function banya h searching ke liye 8 | search() { 9 | const keyword = this.queryStr.keyword ? 10 | { 11 | name: { 12 | $regex: this.queryStr.keyword, 13 | $options: "i", 14 | }, 15 | } 16 | : {}; 17 | 18 | this.query = this.query.find({ ...keyword }); 19 | return this; 20 | } 21 | 22 | // filter nam ka function banaya h filter ke liye 23 | filter() { 24 | const queryCopy = { ...this.queryStr }; 25 | // Removing some fields for category 26 | const removeFields = ["keyword", "page", "limit"]; 27 | removeFields.forEach((key) => delete queryCopy[key]); 28 | 29 | // Filter For Price and Rating 30 | let queryStr = JSON.stringify(queryCopy); 31 | queryStr = queryStr.replace(/\b(gt|gte|lt|lte)\b/g, (key) => `$${key}`); 32 | 33 | this.query = this.query.find(JSON.parse(queryStr)); 34 | return this; 35 | } 36 | 37 | // pagination nam ka hm ne ke function create ker liya h pagin.. mtlb ke hame ek page pe kitne product dekhane h 38 | pagination(resultPerPage) { 39 | 40 | const currentPage = Number(this.queryStr.page) || 1; 41 | 42 | const skip = resultPerPage * (currentPage - 1); 43 | 44 | this.query = this.query.limit(resultPerPage).skip(skip); 45 | 46 | return this; 47 | } 48 | } 49 | 50 | module.exports = ApiFeatures; 51 | -------------------------------------------------------------------------------- /backend/models/productModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const productSchema = new mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: [true, "Please Enter Product Name"], 7 | trim: true, 8 | }, 9 | description: { 10 | type: String, 11 | required: [true, "Please Enter Product Discription"], 12 | }, 13 | price: { 14 | type: Number, 15 | required: [true, "Please Enter Product Price"], 16 | maxLength: [8, "Price cannot exceed 8 characters"], 17 | }, 18 | rating: { 19 | type: Number, 20 | default: 0, 21 | }, 22 | images: [ 23 | { 24 | public_id: { 25 | type: String, 26 | required: true, 27 | }, 28 | url: { 29 | type: String, 30 | required: true, 31 | }, 32 | }, 33 | ], 34 | category: { 35 | type: String, 36 | required: [true, "Please Enter Product Category"], 37 | }, 38 | stock: { 39 | type: String, 40 | required: [4, "Stock cannot exceed 4 characters"], 41 | default: 1, 42 | }, 43 | numOfReviews: { 44 | type: Number, 45 | default: 0, 46 | }, 47 | reviews: [ 48 | { 49 | user: { 50 | type: mongoose.Schema.ObjectId, 51 | required: true, 52 | }, 53 | name: { 54 | type: String, 55 | required: true, 56 | }, 57 | rating: { 58 | type: Number, 59 | required: true, 60 | }, 61 | comment: { 62 | type: String, 63 | required: true, 64 | }, 65 | }, 66 | ], 67 | 68 | user: { 69 | type: mongoose.Schema.ObjectId, 70 | ref: "User", 71 | required: true, 72 | }, 73 | createdAt: { 74 | type: Date, 75 | default: Date.now, 76 | }, 77 | }); 78 | 79 | module.exports = mongoose.model("Product", productSchema); 80 | -------------------------------------------------------------------------------- /backend/models/userModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const validator = require("validator"); 3 | const bcrypt = require("bcryptjs"); 4 | const jwt = require("jsonwebtoken"); 5 | const crypto = require("crypto"); 6 | 7 | const userSchema = new mongoose.Schema({ 8 | name: { 9 | type: String, 10 | required: [true, "Please Enter Your Name"], 11 | maxLength: [30, "Name cannot exceed 30 characters"], 12 | minLength: [4, "Name should have more than 4 characters"], 13 | }, 14 | email: { 15 | type: String, 16 | required: [true, "Please Enter Your Email"], 17 | unique: true, 18 | validate: [validator.isEmail, "Please Enter a valid Email"], 19 | }, 20 | password: { 21 | type: String, 22 | required: [true, "Please Enter Your Password"], 23 | minLength: [8, "Password should be greater than 8 characters"], 24 | select: false, 25 | }, 26 | avatar: { 27 | public_id: { 28 | type: String, 29 | required: true, 30 | }, 31 | url: { 32 | type: String, 33 | required: true, 34 | }, 35 | }, 36 | role: { 37 | type: String, 38 | default: "user", 39 | }, 40 | createdAt: { 41 | type: Date, 42 | default: Date.now, 43 | }, 44 | 45 | resetPasswordToken: String, 46 | resetPasswordExpire: Date, 47 | }); 48 | 49 | userSchema.pre("save", async function (next) { 50 | if (!this.isModified("password")) { 51 | next(); 52 | } 53 | 54 | this.password = await bcrypt.hash(this.password, 10); 55 | }); 56 | 57 | // JWT TOKEN 58 | userSchema.methods.getJWTToken = function () { 59 | return jwt.sign({ id: this._id }, process.env.JWT_SECRET, { 60 | expiresIn: process.env.JWT_EXPIRE, 61 | }); 62 | }; 63 | 64 | // compare password 65 | userSchema.methods.comparePassword = async function (password) { 66 | return await bcrypt.compare(password, this.password); 67 | }; 68 | 69 | // Generating Password Reset Token 70 | userSchema.methods.getResetPasswordToken = function () { 71 | // Generating Token 72 | const resetToken = crypto.randomBytes(20).toString("hex"); 73 | // Hashing and adding resetPasswordToken to userSchema 74 | this.resetPasswordToken = crypto 75 | .createHash("sha256") 76 | .update(resetToken) 77 | .digest("hex"); 78 | 79 | this.resetPasswordExpire = Date.now() + 15 * 60 * 1000; 80 | 81 | return resetToken; 82 | }; 83 | 84 | module.exports = mongoose.model("User", userSchema); 85 | -------------------------------------------------------------------------------- /backend/controllers/productController.js: -------------------------------------------------------------------------------- 1 | const Product = require("../models/productModel"); 2 | const ErrorHander = require("../utils/errorhander"); 3 | const catchAsyncErrors = require("../middleware/catchAsyncErrors"); 4 | const ApiFeatures = require("../utils/apifeatures"); 5 | 6 | // create product -- only dmin do this work 7 | exports.createProduct = catchAsyncErrors(async (req, res, next) => { 8 | req.body.user = req.user.id; 9 | const product = await Product.create(req.body); 10 | res.status(200).json({ 11 | success: true, 12 | product, 13 | }); 14 | }); 15 | 16 | // Get All Product 17 | exports.getAllProducts = catchAsyncErrors(async (req, res) => { 18 | const resultPerPage = 5; 19 | const productCount = await Product.countDocuments(); 20 | const apiFeature = new ApiFeatures(Product.find(), req.query) 21 | .search() 22 | .filter() 23 | .pagination(resultPerPage); 24 | const products = await apiFeature.query; 25 | res.status(200).json({ 26 | success: true, 27 | products, 28 | }); 29 | }); 30 | 31 | // Get Product Details 32 | exports.getProductDetails = catchAsyncErrors(async (req, res, next) => { 33 | const product = await Product.findById(req.params.id); 34 | if (!product) { 35 | return next(new ErrorHander("Product Not Found", 404)); 36 | } 37 | 38 | res.status(200).json({ 39 | success: true, 40 | product, 41 | productCount, 42 | }); 43 | }); 44 | 45 | // Update Product -- only Admin route (keval isko admin he ker sakta h) 46 | exports.updateProduct = catchAsyncErrors(async (req, res, next) => { 47 | let product = await Product.findById(req.params.id); 48 | 49 | if (!product) { 50 | return next(new ErrorHander("Product Not Found", 404)); 51 | } 52 | 53 | product = await Product.findByIdAndUpdate(req.params.id, req.body, { 54 | new: true, 55 | runValidators: true, 56 | useFindAndModify: false, 57 | }); 58 | 59 | res.status(200).json({ 60 | success: true, 61 | product, 62 | }); 63 | }); 64 | 65 | // Delete Product 66 | exports.deleteProduct = catchAsyncErrors(async (req, res, next) => { 67 | const product = await Product.findById(req.params.id); 68 | 69 | if (!product) { 70 | return next(new ErrorHander("Product Not Found", 404)); 71 | } 72 | 73 | await product.remove(); 74 | res.status(200).json({ 75 | success: true, 76 | message: "Product Delete Successfully", 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /backend/controllers/userController.js: -------------------------------------------------------------------------------- 1 | const ErrorHander = require("../utils/errorhander"); 2 | const catchAsyncErrors = require("../middleware/catchAsyncErrors"); 3 | const User = require("../models/userModel"); 4 | const sendToken = require("../utils/jwtToken"); 5 | const sendEmail = require("../utils/sendEmail"); 6 | const crypto = require("crypto"); 7 | 8 | // Registration a users 9 | exports.registerUser = catchAsyncErrors(async (req, res, next) => { 10 | const { name, email, password } = req.body; 11 | const user = await User.create({ 12 | name, 13 | email, 14 | password, 15 | avatar: { 16 | public_id: "This is a single Id", 17 | url: "profiepicUrl", 18 | }, 19 | }); 20 | 21 | sendToken(user, 201, res); 22 | }); 23 | 24 | // Login User 25 | exports.loginUser = catchAsyncErrors(async (req, res, next) => { 26 | const { email, password } = req.body; 27 | if (!email || !password) { 28 | return next(new ErrorHander("Please Enter Email & Password", 400)); 29 | } 30 | const user = await User.findOne({ email }).select("+password"); 31 | 32 | if (!user) { 33 | return next(new ErrorHander("Invalid email or password", 401)); 34 | } 35 | const isPasswordMatched = await user.comparePassword(password); 36 | 37 | if (!isPasswordMatched) { 38 | return next(new ErrorHander("Invalid email or password", 401)); 39 | } 40 | 41 | sendToken(user, 200, res); 42 | }); 43 | 44 | // Logout User 45 | exports.logout = catchAsyncErrors(async (req, res, next) => { 46 | res.cookie("token", null, { 47 | expires: new Date(Date.now()), 48 | httpOnly: true, 49 | }); 50 | 51 | res.status(200).json({ 52 | success: true, 53 | message: "Logged Out", 54 | }); 55 | }); 56 | 57 | // Forgot Password 58 | exports.forgotPassword = catchAsyncErrors(async (req, res, next) => { 59 | const user = await User.findOne({ email: req.body.email }); 60 | if (!user) { 61 | return next(new ErrorHander("User not found", 404)); 62 | } 63 | 64 | // Get ResetPassword Token 65 | const resetToken = user.getResetPasswordToken(); 66 | 67 | await user.save({ validateBeforeSave: false }); 68 | 69 | const resetPasswordUrl = `${req.protocol}://${req.get( 70 | "host" 71 | )}/password/reset/${resetToken}`; 72 | 73 | const message = `Your password reset token is :- \n\n ${resetPasswordUrl} \n\nIf you have not requested this email then, please ignore it.`; 74 | 75 | try { 76 | await sendEmail({ 77 | email: user.email, 78 | subject: `Ecommerce Password Recovery`, 79 | message, 80 | }); 81 | 82 | res.status(200).json({ 83 | success: true, 84 | message: `Email sent to ${user.email} successfully`, 85 | }); 86 | } catch (error) { 87 | user.resetPasswordToken = undefined; 88 | user.resetPasswordExpire = undefined; 89 | 90 | await user.save({ validateBeforeSave: false }); 91 | 92 | return next(new ErrorHander(error.message, 500)); 93 | } 94 | }); 95 | 96 | // Reset Password 97 | exports.resetPassword = catchAsyncErrors(async (req, res, next) => { 98 | const resetPasswordToken = crypto 99 | .createHash("sha256") 100 | .update(req.params.token) 101 | .digest("hex"); 102 | 103 | const user = await User.findOne({ 104 | resetPasswordToken, 105 | resetPasswordExpire: { $gt: Date.now() }, 106 | }); 107 | 108 | if (!user) { 109 | return next( 110 | new ErrorHander( 111 | "Reset Password Token is invalid or has been expired", 112 | 400 113 | ) 114 | ); 115 | } 116 | 117 | if (req.body.password !== req.body.confirmPassword) { 118 | return next(new ErrorHander("Password does not password", 400)); 119 | } 120 | 121 | user.password = req.body.password; 122 | user.resetPasswordToken = undefined; 123 | user.resetPasswordExpire = undefined; 124 | 125 | await user.save(); 126 | 127 | sendToken(user, 200, res); 128 | }); 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MERN_STACK_ECOMMERCE_PROJECT-REACT-REDUX-EXPRESS-NODE-MONGODB-COMPLETE-PART-02 2 | # Hello Everyone, I am Arun Choudhary 3 | 4 | # Backend error handling :- 5 | 1. We have to first create a class error handling, So that it handles all the errors in the backend itself, for which we do not have to write much code, we have to write only 1 line. 6 | 2. Before this we will create a folder utils and inside it we will create a file named errorhander.js. 7 | 3. After this we will create a folder named middleware, in it we will have a file named error in which we will import errorhander.js 8 | and 9 | Now by importing the errorhandle in productController file we will use it to getProductDetails. 10 | 4. After doing all these things, we will aslo go to test on the postman. 11 | 5. The errorhander is complete, but what about async error? -> Whenever we are told to use async/await function, We should always use try/catch block with it. Try block will run only when the code inside it is successfully true., Otherwise the code inside catch will run then we will create an errorhandle for it which will be special for async. 12 | Why are we using ty/catch what type of error will the try/catch block run on? -> For Example:- When this thing will come in handy, when we create the product, if we dosn't put some details in it then it will continue to be processed in the server, so our server will keep getting ruined. 13 | So to solve this problem we use try/catch block so that if it work successfully it will give result, else show error it will work in our try/catch block. 14 | 6. Now these is only one type of error which is -- that when we put the database link wrong, then what kind of error will we get from it this type of error is called Unhandled Promise Rejection. 15 | So the reason for getting this kind of error is that from where our server has crashed, this is the error for that. 16 | Solution :- What we have to do when this kind of error comes that we have to shutdown the server as soon as possible then we will coding for that. 17 | 7.If someone also search for undefined thing on our website the we will show him this undefined error. we call this type of error as UnCaughter error. 18 | 8. Now only the last type of error remains that mangodb error before resolvong it we see what kind of error show us in mongodb. http://localhost:4000/api/v1/product/645 <-- And mongodb know that the product has no such Id. "cast to pbjectId failed for value\*645\*(type string at path\*_id\* for model\"Product\" <-- Will show this kind of error, showing this kind of error because we have put something like this 645 id in place of product id which is not accessible is. 19 | 9. After this errorhander, we will add some feature beacuse right now our website does not have the facility of search filtering and pagination that we can filter through category or can shot according od rating this thing if not, then what is the use of our website, we are going to create it first --> Search, Filter, and Pagination. 20 | 10. For this we will create a file named apifeature.js which will be in the folder named utils, inside it we will create a class named apifeature. 21 | 11. Search Function :- We have used search function because if a user comes to our website and search by entering the name of the product in the search box then all the information related to that product will be shown and if the product which is not in our list then if user search then it will show null to that user. 22 | 12. Filter Function :- We have used filter function because any user will come to our E-ecommerce website and enter any price range related to the product i.e if any person wants to check any product between 10000 to 15000, he/she can check this can use filter. 23 | 13. Pagination Function :- We have created Pagination function because it will work to show limited product only on one page instead of showing together, it works pagination function. 24 | * Even our search, filter, pagination i completed, after that we start user password authentication. * 25 | 26 | # BACKEND USER & PASSWORD AUTHENTICATION:- 27 | # * Now will do backend Authentication * 28 | 14. Basically the model of the user will be created how the user will login and register. 29 | 15. So to make this we have to install some package. like - bcryptjs, jsonwebtoken, validator, nodemailer, cookie-parsar, body-parsar. 30 | 1. bcryptjs :- what is bcrypt :- when we save our or user's password, it will be saved in the database, so before they are saved. we will convert the password to a hash with the help of bcrypt. so that no one can see the correct password of anyone, so we will install bcrypt. 31 | 2. jsonwebtoken :- what will the jsonwebtoken do it is basically used to generate the token. 32 | 3. validator :- validator is neccesary to check whether the data is correct or not, so this module is easy to use and validator data quickly and easily. 33 | 4. Nodemailer :- What does nodemailer do, if a user forwards the password then reaches the link on his email as OTP or as per reset password this is how notemailer works and we don't need to type any special message which it automatically sends. 34 | 16. Even we have finished the work of registration and pasword hash Now after this we will see that if a user registers and we want to get him automatic loging. then we will see how we will do that. 35 | 17. After the registration and token generation is ouer, now we will create the login function. 36 | 18. But let's make a function for authentiaction, we will work in this that the user who is logged in can access the product, for this we will do authentication, for this we will create a file named auth.js inside the middleware in which we will autheticate. 37 | 19. * So for what we have done is that if the user login he/she can see the product get meaning but he/she can not create, update and delete any thing which we have done. And by admin login, he/she can do all the work, he/she can create, update and delete any product all this we have dome in authentication. * 38 | 20. Now we are going to create a method in userModel because w have to provide facility to reset password so if someone forget password then he can also set password by generating reset token like we do by clicking on foerword password. 39 | 21. Then we started creating getresetpasswordToken and forgetpassword functions -- why did we do this? 40 | If a user wants to break the password, then before breaking the password from him, we have to get the reset token again so that we can forget the user. 41 | After doing this, we will create a URL to break the password on the user's email, which we will send to the user's email, so that he can break the password by linking on the link. 42 | 22. Even by sending an email to the user, we asked him to change the password. 43 | 23. After this we will solve the minor errors 44 | 24. And till now our backend authentication is completed 45 | --------------------------------------------------------------------------------