ExpressJs

⌘K
  1. Home
  2. Docs
  3. ExpressJs
  4. moduler project
  5. 01.Project Configuration

01.Project Configuration

First, let’s create a new project and install the necessary dependencies:

mkdir project-root
cd project-root
npm init -y
npm install express sequelize mysql2 bcryptjs jsonwebtoken dotenv
npm install --save-dev nodemon
  • express: সার্ভার তৈরি করতে।
  • sequelize: ডাটাবেস ম্যানেজমেন্টের জন্য।
  • mysql2: MySQL ডাটাবেসের জন্য।
  • bcryptjs: পাসওয়ার্ড এনক্রিপশনের জন্য।
  • jsonwebtoken: টোকেন ব্যবস্থাপনার জন্য।
  • dotenv: পরিবেশগত ভেরিয়েবল ব্যবহার করতে।

Step 1: Set up the project structure

First, create the following folder structure:

project-root/

├── config/
   ├── database.js
   └── index.js

├── modules/
   └── auth/
       ├── controllers/
          └── authController.js
       ├── models/
          ├── user.model.js
          ├── group.model.js
          └── permission.model.js
       ├── routes/
          └── auth.routes.js
       ├── middlewares/
          ├── auth.js
          └── permissions.js
       └── index.js

├── utils/
   └── moduleLoader.js

├── app.js
└── package.json

Step 2: Set up the main application file

Create the app.js file in the project root:

// app.js
const express = require('express');
const dotenv = require('dotenv');
const { loadModules } = require('./utils/moduleLoader');
const db = require('./config/database');

dotenv.config();

const app = express();
app.use(express.json());

// Load all modules
loadModules(app);

const PORT = process.env.PORT || 3000;

db.sequelize.sync().then(() => {
  app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
  });
});

.env ফাইল তৈরি করা

আপনার প্রজেক্ট ফোল্ডারের রুটে একটি .env ফাইল তৈরি করুন এবং সেখানে প্রয়োজনীয় পরিবেশগত ভেরিয়েবল যোগ করুন। উদাহরণ:

# Application Port
PORT=5000

# Database Configuration
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=12345
DB_NAME=express_auth_system

# JWT Secret
JWT_SECRET=your_jwt_secret

উপাদানের ব্যাখ্যা (বাংলা):

  1. PORT=5000: সার্ভার কোন পোর্টে চলবে তা নির্ধারণ করে।
    • এখানে ডিফল্ট পোর্ট হিসেবে ৫০০০ ব্যবহার করা হয়েছে।
  2. ডাটাবেস সংযোগের জন্য ভেরিয়েবল:
    • DB_HOST: আপনার ডাটাবেস সার্ভারের হোস্ট, সাধারণত localhost
    • DB_USER: ডাটাবেস ব্যবহারকারীর নাম।
    • DB_PASSWORD: ডাটাবেসের পাসওয়ার্ড।
    • DB_NAME: আপনার প্রজেক্টের জন্য নির্ধারিত ডাটাবেসের নাম।
  3. JWT_SECRET=your_jwt_secret:
    • JWT (JSON Web Token) তৈরি ও যাচাই করার জন্য একটি সিক্রেট কী। এটি সুরক্ষিত রাখতে হবে।

এই .env ফাইলটি গোপনীয় এবং আপনার প্রজেক্টের বাইরে শেয়ার করা উচিত নয়। .gitignore ফাইল ব্যবহার করে .env ফাইলটি গিটে যোগ করা থেকে রক্ষা করুন।

.gitignore ফাইলের উদাহরণ:

# Ignore .env file
.env

Step 3: Create the module loader utility

Create the utils/moduleLoader.js file:

// utils/moduleLoader.js
const fs = require('fs');
const path = require('path');

function loadModules(app) {
  const modulesPath = path.join(__dirname, '..', 'modules');
  const modules = fs.readdirSync(modulesPath);

  modules.forEach(moduleName => {
    const modulePath = path.join(modulesPath, moduleName);
    if (fs.statSync(modulePath).isDirectory()) {
      const moduleFile = path.join(modulePath, 'index.js');
      if (fs.existsSync(moduleFile)) {
        const module = require(moduleFile);
        if (typeof module === 'function') {
          module(app);
        }
      }
    }
  });
}

module.exports = { loadModules };

Step 4: Set up the database configuration

Create the config/database.js file:

// config/database.js
const { Sequelize } = require('sequelize');
const dotenv = require('dotenv');

dotenv.config();

const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASSWORD, {
  host: process.env.DB_HOST,
  dialect: 'mysql',
});

const db = {};

db.Sequelize = Sequelize;
db.sequelize = sequelize;

module.exports = db;

Step 5: Create the authentication module

Now, let’s create the authentication module using our previous authentication system.

Create the modules/auth/index.js file:

// modules/auth/index.js
const authRoutes = require('./routes/auth.routes');

module.exports = (app) => {
  app.use('/api/auth', authRoutes);
};

Create the modules/auth/models/user.model.js file:

// modules/auth/models/user.model.js
const db = require('../../../config/database');

const User = db.sequelize.define("User", {
  username: {
    type: db.Sequelize.STRING,
    allowNull: false,
    unique: true,
  },
  email: {
    type: db.Sequelize.STRING,
    allowNull: false,
    unique: true,
  },
  password: {
    type: db.Sequelize.STRING,
    allowNull: false,
  },
});

module.exports = User;

Create the modules/auth/models/group.model.js file:

// modules/auth/models/group.model.js
const db = require('../../../config/database');

const Group = db.sequelize.define("Group", {
  name: {
    type: db.Sequelize.STRING,
    allowNull: false,
    unique: true,
  },
});

module.exports = Group;

Create the modules/auth/models/permission.model.js file:

// modules/auth/models/permission.model.js
const db = require('../../../config/database');

const Permission = db.sequelize.define("Permission", {
  name: {
    type: db.Sequelize.STRING,
    allowNull: false,
    unique: true,
  },
  codename: {
    type: db.Sequelize.STRING,
    allowNull: false,
    unique: true,
  },
});

module.exports = Permission;

Create the modules/auth/middlewares/auth.js file:

// modules/auth/middlewares/auth.js
const jwt = require('jsonwebtoken');
const User = require('../models/user.model');

const verifyToken = async (req, res, next) => {
  const token = req.headers['x-access-token'];

  if (!token) {
    return res.status(403).send({ message: "No token provided!" });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    const user = await User.findByPk(decoded.id);
    
    if (!user) {
      return res.status(401).send({ message: "Unauthorized!" });
    }

    req.user = user;
    next();
  } catch (err) {
    return res.status(401).send({ message: "Unauthorized!" });
  }
};

module.exports = verifyToken;

Create the modules/auth/middlewares/permissions.js file:

// modules/auth/middlewares/permissions.js
const User = require('../models/user.model');
const Group = require('../models/group.model');
const Permission = require('../models/permission.model');

const checkPermission = (...requiredPermissions) => {
  return async (req, res, next) => {
    try {
      const user = await User.findByPk(req.user.id, {
        include: [
          {
            model: Permission,
          },
          {
            model: Group,
            include: [Permission],
          },
        ],
      });

      const userPermissions = user.Permissions.map(p => p.codename);
      const groupPermissions = user.Groups.flatMap(g => g.Permissions.map(p => p.codename));
      const allPermissions = [...new Set([...userPermissions, ...groupPermissions])];

      const hasRequiredPermission = requiredPermissions.some(permission => 
        allPermissions.includes(permission)
      );

      if (hasRequiredPermission) {
        next();
      } else {
        res.status(403).send({ message: "You don't have permission to perform this action." });
      }
    } catch (err) {
      res.status(500).send({ message: err.message });
    }
  };
};

module.exports = checkPermission;

Create the modules/auth/controllers/authController.js file:

// modules/auth/controllers/authController.js
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const User = require('../models/user.model');

exports.register = async (req, res) => {
  try {
    const { username, email, password } = req.body;
    const hashedPassword = await bcrypt.hash(password, 8);

    const user = await User.create({
      username,
      email,
      password: hashedPassword,
    });

    res.status(201).send({ message: "User registered successfully!", userId: user.id });
  } catch (err) {
    res.status(500).send({ message: err.message });
  }
};

exports.login = async (req, res) => {
  try {
    const { username, password } = req.body;
    const user = await User.findOne({ where: { username } });

    if (!user) {
      return res.status(404).send({ message: "User not found." });
    }

    const isPasswordValid = await bcrypt.compare(password, user.password);

    if (!isPasswordValid) {
      return res.status(401).send({ message: "Invalid password!" });
    }

    const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, {
      expiresIn: 86400, // 24 hours
    });

    res.status(200).send({
      id: user.id,
      username: user.username,
      email: user.email,
      accessToken: token,
    });
  } catch (err) {
    res.status(500).send({ message: err.message });
  }
};

Create the modules/auth/routes/auth.routes.js file:

// modules/auth/routes/auth.routes.js
const express = require('express');
const authController = require('../controllers/authController');
const verifyToken = require('../middlewares/auth');
const checkPermission = require('../middlewares/permissions');

const router = express.Router();

router.post('/register', authController.register);
router.post('/login', authController.login);

// Example protected route
router.get('/protected', verifyToken, (req, res) => {
  res.status(200).send({ message: "This is a protected route." });
});

// Example route with permission check
router.get('/admin', verifyToken, checkPermission('access_admin'), (req, res) => {
  res.status(200).send({ message: "This is an admin route." });
});

module.exports = router;

Step 6: Set up the package.json file

Create the package.json file in the project root:

{
  "name": "modular-express-app",
  "version": "1.0.0",
  "description": "A modular Express.js application with authentication",
  "main": "app.js",
  "scripts": {
    "start": "node app.js",
    "dev": "nodemon app.js"
  },
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "dotenv": "^10.0.0",
    "express": "^4.17.1",
    "jsonwebtoken": "^8.5.1",
    "mysql2": "^2.3.0",
    "sequelize": "^6.6.5"
  },
  "devDependencies": {
    "nodemon": "^2.0.12"
  }
}

run server

npm start

How can we help?