๐งฐ Tech Stack
Node.js & Express โ Backend framework
Sequelize โ ORM for MySQL
JWT โ Stateless authentication
bcryptjs โ Password hashing
dotenv โ Environment variables
CORS โ Cross-origin resource sharing
๐ Project Structure
src/
โโโ config/ # DB & environment config
โโโ controllers/ # Auth logic
โโโ middlewares/ # Token & role guards
โโโ models/ # Sequelize models
โโโ routes/ # API routes
โโโ server.js # App entry point
โ๏ธ Step 1: Initialize Project
Install Dependencies:
npm install express sequelize mysql2 cors jsonwebtoken bcryptjs dotenv
- Database Configuration with Sequelize
Set up PostgreSQL credentials and define a Sequelize instance. The Tutorial model contains fields for title, description and published status. You can run PostgreSQL locally, including via Docker for convenience in development.
Configure MySQL โ config/database.js:
module.exports = {
HOST: "localhost",
USER: "root",
PASSWORD: "yourpassword",
DB: "jwt_auth_db",
dialect: "mysql",
};
**
๐งฌ Step 2: Define Sequelize Models**
models/user.model.js:
const User = sequelize.define("user", {
username: { type: Sequelize.STRING, unique: true },
email: { type: Sequelize.STRING, unique: true },
password: Sequelize.STRING,
});
models/role.model.js:
const Role = sequelize.define("role", {
name: { type: Sequelize.STRING, unique: true },
});
Define Relationship:
User.belongsToMany(Role, { through: "user_roles" });
Role.belongsToMany(User, { through: "user_roles" });
๐ Step 3: JWT Authentication Logic
โ
Signup Endpoint
- Implementing RESTful API Routes
Create controllers for each CRUD operation: creating, finding (all or by ID), updating and deleting tutorials. Map these controllers to Express routes like /api/tutorials and /api/tutorials/:id. Each endpoint enables a RESTful API ideal for single-page applications.
const bcrypt = require("bcryptjs");
exports.signup = async (req, res) => {
try {
const { username, email, password } = req.body;
const hashedPassword = bcrypt.hashSync(password, 8);
const user = await User.create({ username, email, password: hashedPassword });
const defaultRole = await Role.findOne({ where: { name: "user" } });
await user.addRole(defaultRole);
res.send({ message: "User registered successfully!" });
} catch (err) {
res.status(500).send({ message: err.message });
}
};
๐ Signin Endpoint
const jwt = require("jsonwebtoken");
exports.signin = async (req, res) => {
try {
const user = await User.findOne({ where: { username: req.body.username } });
if (!user) return res.status(404).send({ message: "User not found." });
const valid = bcrypt.compareSync(req.body.password, user.password);
if (!valid) return res.status(401).send({ message: "Invalid password." });
const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: "1h" });
res.status(200).send({
id: user.id,
username: user.username,
email: user.email,
accessToken: token,
});
} catch (err) {
res.status(500).send({ message: err.message });
}
};
๐ก๏ธ Step 4: Middleware for Security
๐ Verify JWT Token
const jwt = require("jsonwebtoken");
exports.verifyToken = (req, res, next) => {
const token = req.headers["x-access-token"];
if (!token) return res.status(403).send({ message: "No token provided!" });
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) return res.status(401).send({ message: "Unauthorized!" });
req.userId = decoded.id;
next();
});
};
๐ง Role-Based Access
exports.isAdmin = async (req, res, next) => {
const user = await User.findByPk(req.userId, { include: ["roles"] });
const isAdmin = user.roles.some(role => role.name === "admin");
if (isAdmin) next();
else res.status(403).send({ message: "Requires Admin Role!" });
};
๐ Step 5: Protect Routes
routes/user.routes.js:
const { verifyToken, isAdmin } = require("../middlewares/authJwt");
router.get("/admin-only", [verifyToken, isAdmin], (req, res) => {
res.send("Admin Content Only");
});
**
๐งช Step 6: Test API with cURL/Postman**
Register User
curl -X POST http://localhost:3000/api/auth/signup \
-H "Content-Type: application/json" \
-d '{"username":"test","email":"test@example.com","password":"123456"}'
Login to Get Token
curl -X POST http://localhost:3000/api/auth/signin \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"123456"}'
Access Protected Route
curl -X GET http://localhost:3000/api/test/admin \
-H "x-access-token: YOUR_JWT_TOKEN"
- Running Your Full-Stack App
Start your Express server on port 3000, ensuring the database syncs via Sequelize on launch. Launch your React application with npm run dev (typically on port 3000). Ensure both backend and frontend are running locally to allow for seamless, real-time development and testing.
๐ Best Security Practices
โ
Use .env for secrets and DB credentials
โ
Limit token lifetime (e.g., 1 hour)
โ
Use helmet to secure HTTP headers
โ
Enable rate limiting
โ
Always hash passwords securely with bcryptjs
โ
Conclusion
By following this guide, you now have:
๐ Secure JWT authentication
๐งฌ MySQL user-role mapping via Sequelize
๐งฑ Role-Based Access Control (RBAC)
โก Production-ready and scalable backend foundation
**
Key Takeaways: Full-Stack JavaScript Best Practices**
The combination of React, Node.js, Express and PostgreSQL is a proven tech stack for dynamic, scalable apps.
Sequelize ORM streamlines database interaction and model management in a Node.js environment.
Modular architecture keeps services, components and routes maintainable and extensible.
Top comments (0)