A clean project structure helps keep your code organized, scalable, and easy to maintain. Hereβs how to structure a Node.js app with JWT-based authentication applied selectively.
project-root/
βββ config/
β βββ env.js # Environment config
βββ controllers/
β βββ authController.js # Login, logout, and token logic
βββ middlewares/
β βββ authMiddleware.js # JWT token validation
βββ routes/
β βββ protectedRoutes.js # Routes requiring authentication
β βββ publicRoutes.js # Open-access routes
βββ utils/
β βββ jwtUtils.js # JWT utility functions
βββ .env # Environment variables
βββ app.js # Main app configuration
βββ server.js # Server setup
config/
env.js
: Load environment variables using dotenv
so they're accessible across the app.
const dotenv = require("dotenv");
dotenv.config();
module.exports = {
jwtSecret: process.env.JWT_SECRET,
};
controllers/
authController.js
: Handle user login, generate JWT, and set it in cookies. Define the logout logic here as well.
const jwt = require("jsonwebtoken");
const { jwtSecret } = require("../config/env");
// Handle login, create JWT token, and set cookie πͺ
const login = (req, res) => {
const { username, password } = req.body;
if (username === "user" && password === "password") {
const token = jwt.sign({ username }, jwtSecret, { expiresIn: "1h" });
res.cookie("auth_token", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
maxAge: 3600000,
});
return res.status(200).json({ message: "π Logged in successfully!" });
} else {
return res.status(401).json({ message: "π« Invalid credentials!" });
}
};
// Clear the token cookie on logout π
const logout = (req, res) => {
res.clearCookie("auth_token");
res.status(200).json({ message: "π Logged out successfully!" });
};
module.exports = { login, logout };
middlewares/
authMiddleware.js
: Middleware for token verification to protect routes selectively.
const jwt = require("jsonwebtoken");
const { jwtSecret } = require("../config/env");
const authenticateToken = (req, res, next) => {
const token = req.cookies.auth_token;
if (!token) return res.status(401).json({ message: "π Access denied!" });
try {
const decoded = jwt.verify(token, jwtSecret);
req.user = decoded;
next();
} catch (err) {
return res.status(403).json({ message: "β Invalid token!" });
}
};
module.exports = authenticateToken;
routes/
protectedRoutes.js
: Protected routes requiring JWT authentication.
const express = require("express");
const router = express.Router();
const authenticateToken = require("../middlewares/authMiddleware");
router.get("/protected", authenticateToken, (req, res) => {
res
.status(200)
.json({ message: "π Protected data access granted!", user: req.user });
});
module.exports = router;
publicRoutes.js
: Public routes that anyone can access.
const express = require("express");
const router = express.Router();
router.get("/public", (req, res) => {
res.status(200).json({ message: "π Public data accessible to all!" });
});
module.exports = router;
utils/
jwtUtils.js
: Utility functions for JWT handling.
const jwt = require("jsonwebtoken");
const { jwtSecret } = require("../config/env");
const generateToken = (payload) =>
jwt.sign(payload, jwtSecret, { expiresIn: "1h" });
module.exports = { generateToken };
.env
Store your environment variables like JWT_SECRET
here.
JWT_SECRET=your_secret_key_here
app.js