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