Best Practices MERN Stack - cycotechnolgies/Online-Clothing-Shop GitHub Wiki
- Project Structure
- MongoDB Best Practices
- Express.js Best Practices
- React.js Best Practices
- Node.js Best Practices
- Security Best Practices
- Performance Optimization
- Development Workflow
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
// 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
}
});
- Use indexes for frequently queried fields
- Implement data validation at the schema level
- Use aggregation pipeline for complex queries
- Implement proper error handling
- 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' } } }
]);
// 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/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;
// 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;
// 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 };
};
// 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
};
// 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;
- 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' });
}
};
- 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);
};
- 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],
);
- MongoDB Indexing
// Creating compound index
userSchema.index({ email: 1, createdAt: -1 });
// Text index for search
productSchema.index({ name: 'text', description: 'text' });
// 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');
});
});
// 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);
}
};
-
Backend
- express-rate-limit
- helmet
- morgan
- cors
- winston
- mongoose
- jsonwebtoken
-
Frontend
- axios
- react-query
- formik or react-hook-form
- yup
- styled-components or @emotion/styled
- redux-toolkit (if needed)
-
Code Quality
- ESLint
- Prettier
- Husky (pre-commit hooks)
-
Testing
- Jest
- React Testing Library
- Supertest
-
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
-
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.