π§° 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)