full-stack level, production-grade, well-structured, Node.js backend CRUD app, following:
- Best coding practices
- Clean code architecture
- Proper folder structure
- JWT authentication and security
✅ This is backend setup with all essential things.
✨ Let's design it step-by-step:
📦 1. Folder Structure
backend/
├── src/
│ ├── config/ # Configuration files (db, jwt, etc.)
│ ├── controllers/ # Route handlers
│ ├── middlewares/ # Express middlewares (auth, error handling)
│ ├── models/ # Mongoose/Sequelize Models (MongoDB/Postgres)
│ ├── routes/ # API routes
│ ├── services/ # Business logic (separate from controller)
│ ├── utils/ # Utility functions
│ ├── app.js # Express app setup
│ ├── server.js # Start server
├── .env # Environment variables
├── .gitignore
├── package.json
├── README.md
🛠 2. Stack & Libraries Used
- Node.js (runtime)
- Express.js (web framework)
- MongoDB + Mongoose (Database layer)
- bcryptjs (for password hashing)
- jsonwebtoken (for JWT authentication)
- dotenv (for environment variables)
- express-validator (for input validation)
- helmet, cors (for security)
⚙️ 3. Let's Start Coding
➡️ package.json
npm init -y
npm install express mongoose bcryptjs jsonwebtoken dotenv cors helmet express-validator
npm install --save-dev nodemon
Add scripts
:
"scripts": {
"start": "node src/server.js",
"dev": "nodemon src/server.js"
}
➡️ .env
PORT=5000
MONGO_URI=your_mongodb_connection_string
JWT_SECRET=your_jwt_secret_key
JWT_EXPIRES_IN=1d
➡️ src/config/db.js
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI);
console.log('MongoDB Connected');
} catch (error) {
console.error('MongoDB connection failed:', error.message);
process.exit(1);
}
};
module.exports = connectDB;
➡️ src/models/User.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
minlength: 2
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true
},
password: {
type: String,
required: true,
minlength: 6
}
}, { timestamps: true });
module.exports = mongoose.model('User', userSchema);
➡️ src/controllers/authController.js
const User = require('../models/User');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
// Register
exports.register = async (req, res) => {
const { name, email, password } = req.body;
try {
const userExists = await User.findOne({ email });
if (userExists) return res.status(400).json({ message: 'User already exists' });
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
const user = await User.create({
name,
email,
password: hashedPassword
});
res.status(201).json({ user });
} catch (err) {
res.status(500).json({ message: err.message });
}
};
// Login
exports.login = async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.findOne({ email });
if (!user) return res.status(400).json({ message: 'Invalid credentials' });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).json({ message: 'Invalid credentials' });
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRES_IN });
res.status(200).json({ token });
} catch (err) {
res.status(500).json({ message: err.message });
}
};
➡️ src/middlewares/authMiddleware.js
const jwt = require('jsonwebtoken');
const User = require('../models/User');
const protect = async (req, res, next) => {
let token;
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
try {
token = req.headers.authorization.split(' ')[1];
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id).select('-password');
next();
} catch (error) {
res.status(401).json({ message: 'Not authorized, token failed' });
}
}
if (!token) {
res.status(401).json({ message: 'Not authorized, no token' });
}
};
module.exports = { protect };
➡️ src/controllers/userController.js
const User = require('../models/User');
// Get all users (for admin/demo)
exports.getUsers = async (req, res) => {
try {
const users = await User.find().select('-password');
res.status(200).json(users);
} catch (err) {
res.status(500).json({ message: err.message });
}
};
➡️ src/routes/authRoutes.js
const express = require('express');
const { register, login } = require('../controllers/authController');
const { body } = require('express-validator');
const router = express.Router();
router.post('/register', [
body('name').notEmpty().withMessage('Name is required'),
body('email').isEmail().withMessage('Email must be valid'),
body('password').isLength({ min: 6 }).withMessage('Password must be at least 6 characters')
], register);
router.post('/login', [
body('email').isEmail(),
body('password').exists()
], login);
module.exports = router;
➡️ src/routes/userRoutes.js
const express = require('express');
const { getUsers } = require('../controllers/userController');
const { protect } = require('../middlewares/authMiddleware');
const router = express.Router();
router.get('/', protect, getUsers);
module.exports = router;
➡️ src/app.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const authRoutes = require('./routes/authRoutes');
const userRoutes = require('./routes/userRoutes');
const app = express();
// Middlewares
app.use(express.json());
app.use(cors());
app.use(helmet());
// Routes
app.use('/api/auth', authRoutes);
app.use('/api/users', userRoutes);
app.get('/', (req, res) => {
res.send('API is running...');
});
module.exports = app;
➡️ src/server.js
require('dotenv').config();
const app = require('./app');
const connectDB = require('./config/db');
const PORT = process.env.PORT || 5000;
// Connect DB
connectDB();
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
✅ Features included
Feature | Status |
---|---|
Register User | ✅ |
Login User with JWT | ✅ |
Protected Routes | ✅ |
Input Validation | ✅ |
Error Handling Middleware | 🚀 (could be added separately if you want) |
Security (Helmet, CORS) | ✅ |
Clean Service-Controller Separation | ✅ |
npm install
npm run dev
API endpoints will be running at: http://localhost:5000