Tech Stack
-
Express.js
-
MongoDB + Mongoose
-
JWT for auth
-
bcrypt for password hashing
-
express-validator for validation
-
helmet for headers
-
dotenv for environment config
-
express-rate-limit for rate limiting
1. Project Structure
pwc-secure-api/
├── config/
│ └── db.js
├── controllers/
│ └── auth.controller.js
├── middlewares/
│ ├── auth.middleware.js
│ └── error.middleware.js
├── models/
│ └── user.model.js
├── routes/
│ └── auth.routes.js
├── utils/
│ └── generateToken.js
├── .env
├── app.js
├── server.js
├── package.json
2. Install Dependencies
bashnpm init -y npm install express mongoose dotenv bcryptjs jsonwebtoken express-validator helmet express-rate-limit cors
3. Sample Files
.env
envPORT=5000 MONGO_URI=mongodb://localhost:27017/secure-api JWT_SECRET=supersecretkey
config/db.js
js
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI);
console.log('MongoDB connected');
} catch (error) {
console.error('DB connection failed', error);
process.exit(1);
}
};
module.exports = connectDB;
models/user.model.js
js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const userSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true },
password: { type: String, required: true }
});
// Hash password before save
userSchema.pre('save', async function (next) {
if (!this.isModified('password')) return next();
this.password = await bcrypt.hash(this.password, 10);
next();
});
module.exports = mongoose.model('User', userSchema);
utils/generateToken.js
js
const jwt = require('jsonwebtoken');
const generateToken = (userId) => {
return jwt.sign({ id: userId }, process.env.JWT_SECRET, { expiresIn: '1h' });
};
module.exports = generateToken;
middlewares/auth.middleware.js
js
const jwt = require('jsonwebtoken');
const protect = (req, res, next) => {
const token = req.headers.authorization?.split(" ")[1];
if (!token) return res.status(401).json({ message: "Not authorized" });
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded.id;
next();
} catch {
res.status(401).json({ message: "Invalid token" });
}
};
module.exports = { protect };
controllers/auth.controller.js
js
const User = require('../models/user.model');
const bcrypt = require('bcryptjs');
const generateToken = require('../utils/generateToken');
exports.register = async (req, res) => {
const { email, password } = req.body;
const exists = await User.findOne({ email });
if (exists) return res.status(400).json({ message: "User exists" });
const user = await User.create({ email, password });
res.status(201).json({ token: generateToken(user._id) });
};
exports.login = async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user || !(await bcrypt.compare(password, user.password))) {
return res.status(400).json({ message: "Invalid credentials" });
}
res.json({ token: generateToken(user._id) });
};
exports.profile = async (req, res) => {
const user = await User.findById(req.user).select("-password");
res.json(user);
};
routes/auth.routes.js
js
const express = require('express');
const { register, login, profile } = require('../controllers/auth.controller');
const { body } = require('express-validator');
const { protect } = require('../middlewares/auth.middleware');
const router = express.Router();
router.post('/register',
body('email').isEmail(),
body('password').isLength({ min: 6 }),
register
);
router.post('/login', login);
router.get('/profile', protect, profile);
module.exports = router;
app.js
js
const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const cors = require('cors');
const authRoutes = require('./routes/auth.routes');
const { validationResult } = require('express-validator');
const app = express();
app.use(helmet());
app.use(cors());
app.use(express.json());
// Rate limiting
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
// Validation error handler
app.use((req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
});
app.use('/api/auth', authRoutes);
module.exports = app;
server.js
js
require('dotenv').config();
const app = require('./app');
const connectDB = require('./config/db');
connectDB();
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Want to Add More?
I can help you add:
-
Refresh tokens
-
2FA with OTP
-
Roles/Permissions
-
Swagger API docs
-
Unit testing with Jest or Mocha
No comments:
Post a Comment