Best Practices MERN Stack - cycotechnolgies/Online-Clothing-Shop GitHub Wiki

MERN Stack Best Practices and Development Guide

Table of Contents

  1. Project Structure
  2. MongoDB Best Practices
  3. Express.js Best Practices
  4. React.js Best Practices
  5. Node.js Best Practices
  6. Security Best Practices
  7. Performance Optimization
  8. Development Workflow

Project Structure

Recommended Project Structure

mern-project/
├── client/                 # React frontend
│   ├── public/
│   ├── src/
│   │   ├── assets/
│   │   ├── components/
│   │   ├── contexts/
│   │   ├── hooks/
│   │   ├── pages/
│   │   ├── services/
│   │   ├── utils/
│   │   ├── App.js
│   │   └── index.js
│   └── package.json
├── server/                 # Node.js/Express backend
│   ├── config/
│   ├── controllers/
│   ├── middleware/
│   ├── models/
│   ├── routes/
│   ├── services/
│   ├── utils/
│   ├── app.js
│   └── package.json
├── .env.example
├── .gitignore
└── README.md

MongoDB Best Practices

Schema Design

// Good Practice
const userSchema = new Schema({
  email: {
    type: String,
    required: true,
    unique: true,
    trim: true,
    lowercase: true
  },
  password: {
    type: String,
    required: true,
    minlength: 8
  },
  role: {
    type: String,
    enum: ['user', 'admin'],
    default: 'user'
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
});

MongoDB Tips

  1. Use indexes for frequently queried fields
  2. Implement data validation at the schema level
  3. Use aggregation pipeline for complex queries
  4. Implement proper error handling
  5. Use MongoDB Atlas for production deployments
// Example of creating an index
userSchema.index({ email: 1 });

// Example of aggregation pipeline
const result = await Order.aggregate([
  { $match: { status: 'completed' } },
  { $group: { _id: '$userId', total: { $sum: '$amount' } } }
]);

Express.js Best Practices

Route Structure

// routes/user.routes.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/user.controller');
const auth = require('../middleware/auth');

router.post('/register', userController.register);
router.post('/login', userController.login);
router.get('/profile', auth, userController.getProfile);

module.exports = router;

Middleware Pattern

// middleware/error.middleware.js
const errorHandler = (err, req, res, next) => {
  console.error(err.stack);
  
  const status = err.status || 500;
  const message = err.message || 'Something went wrong!';
  
  res.status(status).json({
    success: false,
    status,
    message,
    stack: process.env.NODE_ENV === 'development' ? err.stack : {}
  });
};

module.exports = errorHandler;

React.js Best Practices

Component Structure

// components/Button/Button.jsx
import React from 'react';
import PropTypes from 'prop-types';
import styles from './Button.module.css';

const Button = ({ children, onClick, variant = 'primary', disabled }) => {
  return (
    <button
      className={`${styles.button} ${styles[variant]}`}
      onClick={onClick}
      disabled={disabled}
    >
      {children}
    </button>
  );
};

Button.propTypes = {
  children: PropTypes.node.isRequired,
  onClick: PropTypes.func,
  variant: PropTypes.oneOf(['primary', 'secondary', 'danger']),
  disabled: PropTypes.bool
};

export default Button;

Custom Hook Example

// hooks/useAuth.js
import { useState, useEffect } from 'react';
import api from '../services/api';

export const useAuth = () => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const checkAuth = async () => {
      try {
        const response = await api.get('/auth/me');
        setUser(response.data);
      } catch (error) {
        setUser(null);
      } finally {
        setLoading(false);
      }
    };

    checkAuth();
  }, []);

  return { user, loading };
};

Node.js Best Practices

Environment Configuration

// config/config.js
require('dotenv').config();

module.exports = {
  port: process.env.PORT || 5000,
  mongoUri: process.env.MONGO_URI,
  jwtSecret: process.env.JWT_SECRET,
  nodeEnv: process.env.NODE_ENV || 'development',
  // Add other configuration variables
};

API Response Format

// utils/apiResponse.js
class ApiResponse {
  static success(res, data, message = 'Success', status = 200) {
    return res.status(status).json({
      success: true,
      message,
      data
    });
  }

  static error(res, message = 'Error', status = 400) {
    return res.status(status).json({
      success: false,
      message
    });
  }
}

module.exports = ApiResponse;

Security Best Practices

  1. Authentication & Authorization
// middleware/auth.js
const jwt = require('jsonwebtoken');
const config = require('../config/config');

const auth = (req, res, next) => {
  const token = req.header('Authorization')?.replace('Bearer ', '');
  
  if (!token) {
    return res.status(401).json({ message: 'No token provided' });
  }

  try {
    const decoded = jwt.verify(token, config.jwtSecret);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ message: 'Invalid token' });
  }
};
  1. Input Validation
// validation/user.validation.js
const Joi = require('joi');

const registerValidation = (data) => {
  const schema = Joi.object({
    email: Joi.string().email().required(),
    password: Joi.string().min(8).required(),
    name: Joi.string().min(2).required()
  });

  return schema.validate(data);
};

Performance Optimization

  1. React Performance Tips
// Using React.memo for preventing unnecessary renders
const MemoizedComponent = React.memo(({ prop1, prop2 }) => {
  return (
    // Component JSX
  );
});

// Using useMemo for expensive calculations
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

// Using useCallback for memoizing functions
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);
  1. MongoDB Indexing
// Creating compound index
userSchema.index({ email: 1, createdAt: -1 });

// Text index for search
productSchema.index({ name: 'text', description: 'text' });

Development Workflow

API Testing with Jest

// tests/user.test.js
describe('User API', () => {
  test('should register a new user', async () => {
    const res = await request(app)
      .post('/api/users/register')
      .send({
        email: '[email protected]',
        password: 'password123',
        name: 'Test User'
      });

    expect(res.status).toBe(201);
    expect(res.body).toHaveProperty('token');
  });
});

Error Handling

// controllers/user.controller.js
const register = async (req, res, next) => {
  try {
    const { email, password } = req.body;
    
    // Check if user exists
    const existingUser = await User.findOne({ email });
    if (existingUser) {
      throw new Error('User already exists');
    }
    
    // Create user
    const user = await User.create({ email, password });
    
    // Generate token
    const token = user.generateAuthToken();
    
    return ApiResponse.success(res, { user, token }, 'User registered successfully', 201);
  } catch (error) {
    next(error);
  }
};

Additional Resources and Tools

Recommended Packages

  1. Backend

    • express-rate-limit
    • helmet
    • morgan
    • cors
    • winston
    • mongoose
    • jsonwebtoken
  2. Frontend

    • axios
    • react-query
    • formik or react-hook-form
    • yup
    • styled-components or @emotion/styled
    • redux-toolkit (if needed)

Development Tools

  1. Code Quality

    • ESLint
    • Prettier
    • Husky (pre-commit hooks)
  2. Testing

    • Jest
    • React Testing Library
    • Supertest

Production Considerations

  1. Deployment Checklist

    • Set up proper environment variables
    • Enable compression
    • Implement caching strategies
    • Set up logging and monitoring
    • Configure security headers
    • Set up CI/CD pipeline
  2. Monitoring

    • Use Morgan for HTTP request logging
    • Implement Winston for application logging
    • Set up error tracking (e.g., Sentry)
    • Monitor performance metrics

Remember to always follow these practices while developing your MERN stack application to ensure maintainable, secure, and scalable code.

⚠️ **GitHub.com Fallback** ⚠️