Skip to main content

Command Palette

Search for a command to run...

5. AUTHENTICATION , AUTHORIZAION , Pagination - Node.js + Express + MongoDB

Updated
6 min read
  1. Folder Structure (MVC Pattern)

project/
│── config/
│   └── db.js           # MongoDB connection
│
│── controllers/
│   └── userController.js  # Request handling logic
│
│── models/
│   └── User.js         # Mongoose schema
│
│── routes/
│   └── userRoutes.js   # Routes for user APIs
│
│── middleware/
│   └── errorMiddleware.js # Error handling middleware
│
│── server.js           # Main entry point
│── package.json
│── .env                # Environment variables
  1. Install Dependecnies

npm init -y
npm install express mongoose dotenv nodemon bcryptjs
  1. Connect MongoDB

const mongoose = require('mongoose');
require('dotenv').config();
const dbConnect = ()=>{
    mongoose.connect(process.env.DATABASE_URL)
    .then(()=>{console.log("DB CONNECTION SUCESSFULL")})
    .catch((err)=>{
        console.log("Failed to connect DB")
        console.log(err);
        process.exit(1);
    })
}
module.exports = dbConnect;
  1. Create Model

  2.    const mongoose = require('mongoose')
    
       const userSchema = new mongoose.Schema({
    
           name:{
               type:String,
               required:true
           },
    
           email:{
               type:String,
               required:true
           },
    
           password:{
               type:String,
               required:true
           },
    
           accountType:{  // for authorization
               type:String,
               enum:["Admin" , "Student" , "Instructor"],
               required:true
           },
       }, { timestamps: true })
       module.exports = mongoose.model("User" , userSchema)
    
  3. Controller (Business Logic)

//signup
exports.signup = async(req,res)=>{
    try{
        //Step 1 : fetch data (also fetch otp we provide it in input box once all credetial filled)
        const{name , email , password ,accountType} = req.body;
        //Step 2: Check validations
        //(i) check all filed filled or nor
        if(!name || !password   || !email || !accountType){
            return res.status(401).json({
                success:false,
                message:"All fields required"
            })
        }  
        //(ii) check password and confirm password are same or not 
         //(iii) check user already exist or not -> alreadyexist Please login
         const existingUser = await User.findOne({email});
         if(existingUser){
            return res.status(401).json({
                success:true,
                message:"User already exist! Please Login"
            })
        }
        //Step 4: Hash the password -> npm i bcryptjs
        const hasedPassword = await bcrypt.hash(password , 10);
        //Task 2.1
        const profile = await Profile.create({
            gender:null,
            dateOfBirth:null,
            about:null,
            contactNumber:null
        })
        //Step 5: create entry in DB
        const user = await User.create({
            name ,
            email,
            password:hasedPassword, 
            accountType ,
        })
        //Step 6: return success response
          return res.status(200).json({
            success:true,
            message: "User Ragistered Successfully",
            user
        })
    }
    catch(err){
        console.log(err);
        return res.status(200).json({
            success:false,
            message:'User can not be ragistered ! Please try again',
            error:err.message
        })
    }
}
//login API
exports.login = async(req,res)=>{
    try{
        //Step 1. fetch data from req.body
        const{email , password} = req.body;
        //Step2. Check Validation
         //(i) check all fields are filled or not
         if(!email || !password){
            return res.status(401).json({
                success:false,
                message:"All fields are required"
            })
         }
        //(ii) check user exist or not
        const user = await User.findOne({email}) //this user contain all info of user
        if(!user){
            return res.status(401).json({
                success:false,
                message:"User not found! Please signup"
            })
        }
         ////(iii) check entred passwrod and actual password matched or not if matchedd then create token 
        if(await bcrypt.compare(password , user.password)){
            //create token
            const payload={
                email:user.email,
                id:user._id,
                accountType:user.accountType
            } //token can hold this payload
            const token = jwt.sign(payload ,process.env.JWT_SECRET, {expiresIn:"2h"}); // npm i jsonwebtoken
            const userObj = user.toObject();  //user.toObject() converts the Mongoose document into a plain JavaScript object that can be safely modified.
            userObj.token = token
            userObj.password = undefined
            return res.status(200).json({
                success:true,
                message:"Login Sucessfully",
                userObj
            })
         }
         else{
             return res.status(401).json({
                success:false,
                message:'Password is incorrect'
            })
       }
    }
    catch(err){
        console.log(err);
        return res.status(500).json({
            sucess:false,
            message:"Internal Server Error in login"
        })
    }
}
  1. Routes

const express = require('express')
const router = express.Router();

const{sendOTP,signup,login, getUserDetails } = require('../controllers/Auth')
const {forgotPasswordToken} = require('../controllers/ForgotPassword');
const { auth } = require('../middlewares/auth');

router.post('/signup' , signup);
router.post('/login', login)

module.exports = router
  1. MiddleWare - Auth

const jwt = require('jsonwebtoken')
require('dotenv').config()
exports.auth = async(req,res , next)=>{
    try{
        // Extracting JWT from request cookies, body or header
        const token = req?.body.token || req.header("Authorization")?.replace("Bearer ", "");//replace our Bearer + space with ""
        // If JWT is missing, return 401 Unauthorized response
        if(!token){
            return res.status(401).json({
                sucess:false,
                message:"Token missing"
            })
        }
        try{
            // Verifying the JWT using the secret key stored in environment variables
            const decode = await jwt.verify(token , process.env.JWT_SECRET);
            req.user = decode;
        }
        catch(err){
            return res.status(401).json({
                sucess:false,
                message:"Token is invalid"
            })
        }
        next();
    }
    catch(err){
        console.log(err)
        return res.status(500).json({
            success:false,
            message:"Internal Server Error in auth"
        })
    }
}
  1. PAGINATION

    Pagination = dividing a large dataset into smaller “pages” so that clients don’t have to fetch everything at once.

    Example: You have 10,000 users in DB → Instead of sending all at once, you send 10 users per request.

    • Page 1 → Users 1–10

    • Page 2 → Users 11–20

    • Page 3 → Users 21–30

KEY PARAMETERS:

We usually use two values:

  • page → which page the user wants (1, 2, 3…)

  • limit → how many items per page

    From these, we calculate:

    • skip = (page - 1) * limit
const express = require('express');
const mongoose = require('mongoose');
const app = express();

// Example User Schema
const UserSchema = new mongoose.Schema({
  name: String,
  email: String,
  createdAt: { type: Date, default: Date.now }
});
const User = mongoose.model('User', UserSchema);
// GET users with pagination + sorting
app.get('/users', async (req, res) => {
  try {
    let { page, limit, sortBy, order } = req.query; //passed in params
    // default values
    page = parseInt(page) || 1;
    limit = parseInt(limit) || 10;
    const skip = (page - 1) * limit; // calculate skip
    // sorting logic
    // default sort by createdAt descending (latest first)
    const sortField = sortBy || "createdAt"; //sortField → field to sort by (e.g., name, email, createdAt)

    const sortOrder = order === "asc" ? 1 : -1; // asc = 1, desc = -1
    // query users
    const users = await User.find()
      .sort({ [sortField]: sortOrder })
      .skip(skip)
      .limit(limit);
    // total documents
    const total = await User.countDocuments();
    res.json({
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
      sortBy: sortField,
      order: sortOrder === 1 ? "asc" : "desc",
      data: users
    });
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});
app.listen(5000, () => console.log("Server running on port 5000"));
  • If you don’t use sorting → you might get users in a random order depending on how they were inserted.

  • If you use sorting → you can control the order (e.g., newest first, alphabetical order, etc.).

FrontEnd Logic

import React, { useEffect, useState } from "react";
import axios from "axios";

function UserTable() {
  const [users, setUsers] = useState([]);
  const [page, setPage] = useState(1);
  const [limit] = useState(5); // fixed items per page
  const [totalPages, setTotalPages] = useState(0);
  const [sortBy, setSortBy] = useState("createdAt");
  const [order, setOrder] = useState("desc");\
  // Fetch users
  useEffect(() => {
    fetchUsers();
  }, [page, sortBy, order]);
  const fetchUsers = async () => {
    const res = await axios.get("http://localhost:5000/users", {
      params: { page, limit, sortBy, order }
    });
    setUsers(res.data.data);
    setTotalPages(res.data.totalPages);
  };
  // Change sorting
  const handleSort = (field) => {
    if (sortBy === field) {
      // toggle order asc <-> desc
      setOrder(order === "asc" ? "desc" : "asc");
    } else {
      setSortBy(field);
      setOrder("asc"); // default to ascending when switching field
    }
  };
  return (
    <div className="p-4">
      <h2 className="text-xl font-bold mb-4">Users</h2>

      {/* Table */}
      <table className="border w-full">
        <thead>
          <tr>
            <th className="p-2 cursor-pointer" onClick={() => handleSort("name")}>
              Name {sortBy === "name" && (order === "asc" ? "▲" : "▼")}
            </th>
            <th className="p-2 cursor-pointer" onClick={() => handleSort("email")}>
              Email {sortBy === "email" && (order === "asc" ? "▲" : "▼")}
            </th>
            <th className="p-2 cursor-pointer" onClick={() => handleSort("createdAt")}>
              Created At {sortBy === "createdAt" && (order === "asc" ? "▲" : "▼")}
            </th>
          </tr>
        </thead>
        <tbody>
          {users.map((u) => (
            <tr key={u._id} className="border-t">
              <td className="p-2">{u.name}</td>
              <td className="p-2">{u.email}</td>
              <td className="p-2">{new Date(u.createdAt).toLocaleDateString()}</td>
            </tr>
          ))}
        </tbody>
      </table>
      {/* Pagination controls */}
      <div className="mt-4 flex gap-2">
        <button
          disabled={page === 1}
          onClick={() => setPage(page - 1)}
          className="px-3 py-1 border rounded disabled:opacity-50"
        >
          Prev
        </button>
        <span> Page {page} of {totalPages} </span>
        <button
          disabled={page === totalPages}
          onClick={() => setPage(page + 1)}
          className="px-3 py-1 border rounded disabled:opacity-50"
        >
          Next
        </button>
      </div>
    </div>
  );
}
export default UserTable;

More from this blog

Node.JS - Basic to Advance

10 posts