Coding Standards - Durjoy01/Cholo_jai GitHub Wiki
- Always use custom hooks for reusable logic to promote modularity.
code import { useState, useEffect } from 'react';
const useFetchData = (url) => { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null);
useEffect(() => { const fetchData = async () => { try { const response = await fetch(url); if (!response.ok) throw new Error('Network response was not ok'); const result = await response.json(); setData(result); } catch (err) { setError(err); } finally { setLoading(false); } };fetchData();},[url]);
return { data, loading, error }; };
-Name event handler functions with the handle prefix.
const handleClick = () => { // handle click event };
return Click Me;
Use concise ternary operators or logical && for conditional rendering // Good return isLoggedIn ? : ;
// Avoid if (isLoggedIn) { return ; } else { return ; }
Comments:
- Write comments to explain non-obvious code.
- Use // TODO: to flag sections needing further work or refactoring.
Use Error Boundaries for critical error handling:
class ErrorBoundary extends React.Component { state = { hasError: false };
static getDerivedStateFromError(error) { return { hasError: true }; }
componentDidCatch(error, info) { // log error }
render() { if (this.state.hasError) { return Something went wrong.; } return this.props.children; } }
Write unit tests with Jest and React Testing Library import { render, screen } from '@testing-library/react'; import MyComponent from './MyComponent';
test('renders the component', () => { render(); const element = screen.getByText(/Hello/); expect(element).toBeInTheDocument(); });
#Let's follow a consistent indentation style (e.g., 2 spaces). Use a consistent naming convention:
- Variables and functions: camelCase.
- Constants: UPPER_CASE.
- Class names: PascalCase.
#Code Style:
- Use a linter like ESLint to enforce JavaScript style rules.
- Follow Prettier for formatting rules (optional but useful).
- Use strict mode ('use strict';) at the top of files.
- Prefer arrow functions when dealing with callbacks or anonymous functions.
-Use a consistent indentation style (2 spaces). -Use a linter like ESLint to enforce JavaScript style rules and Prettier for automatic formatting.
.eslintrc.json { "rules": { "indent": ["error", 2] } }
-Always use a central error-handling middleware.
app.use((err, req, res, next) => { console.error(err.stack); res.status(err.status || 500).json({ message: err.message || 'Internal Server Error', error: process.env.NODE_ENV === 'development' ? err : {} }); });
-Use try-catch blocks for async operations:
app.get('/users', async (req, res, next) => { try { const users = await User.find(); res.json(users); } catch (error) { next(error); } });
-Use middlewares for logging, authentication, validation, etc. For example, a logging middleware
// loggingMiddleware.js
const loggingMiddleware = (req, res, next) => {
console.log(${req.method} ${req.url}
);
next();
};
app.use(loggingMiddleware);
-Define routes in separate files and keep them modular:
const express = require('express'); const router = express.Router();
router.get('/users', getUsers); router.post('/users', createUser);
module.exports = router;
-Store sensitive data in environment variables using a .env file:
PORT=3000 DB_HOST=localhost
Use dotenv to load them require('dotenv').config(); const port = process.env.PORT;
-Use helmet to set HTTP headers for security:
bash: npm install helmet js: const helmet = require('helmet'); app.use(helmet()); a) Validate user inputs with libraries like Joi or express-validator. b) Avoid using eval() and unsanitized inputs.
-Use async/await over callbacks for asynchronous operations: app.get('/users', async (req, res, next) => { try { const users = await User.find(); res.json(users); } catch (error) { next(error); } });
Handle promise rejections properly: process.on('unhandledRejection', (reason, promise) => { console.error('Unhandled Rejection:', reason); });
-Use morgan for logging HTTP requests.
bash: npm install morgan
js: const morgan = require('morgan'); app.use(morgan('dev'));
- Write unit tests for controllers and services using libraries like Mocha, Jest, or Chai.
- Perform integration tests for routes
-Comments:
- Use meaningful comments to explain complex logic.
- Avoid unnecessary or redundant comments // This handles user login logic
Call app.listen() only in one place (e.g., in app.js or server.js).
Example app.js:
require('dotenv').config(); const express = require('express'); const helmet = require('helmet'); const morgan = require('morgan'); const userRoutes = require('./routes/users'); const app = express();
// Middleware app.use(express.json()); app.use(helmet()); app.use(morgan('dev'));
// Routes app.use('/api/v1/users', userRoutes);
// Error handling app.use((err, req, res, next) => { res.status(err.status || 500).json({ message: err.message || 'Internal Server Error', }); });
// Start the server
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(Server running on port ${port}
);
});
1.1 Use Appropriate Data Types Always use the correct data type for fields. For example, use Number for numeric data, Boolean for true/false values, and Date for date fields.
1.2 Limit Document Size Ensure that documents remain within an optimal size (ideally less than 1MB) to maintain performance, though MongoDB supports documents up to 16MB.
1.3 Design for Access Patterns Structure documents based on how data is accessed: Embed data in the same document when related data is always accessed together. Use references for one-to-many or many-to-many relationships to avoid excessive duplication.
1.4 Normalize When Necessary Avoid data duplication in cases where updates to multiple documents would be costly or inconsistent. Use normalization with references instead of embedding.
1.5 Versioning Use a versioning scheme for your schema if breaking changes are anticipated. This could involve adding a schemaVersion field to documents.
**code: ** const userSchema = new mongoose.Schema({ firstName: { type: String, required: true }, lastName: { type: String, required: true }, email: { type: String, required: true, unique: true }, });
2.1 Collection Names Use plural nouns for collections (e.g., users, orders). Use lowercase letters and separate words with underscores (snake_case), or camelCase if preferred.
2.2 Field Names Use camelCase for field names (e.g., firstName, orderDate). Be descriptive with field names and avoid abbreviations (firstName instead of fName).
2.3 Index Naming Name your indexes logically based on the fields they index. For example, use idx_users_email for an index on the email field in the users collection.
2.4 Avoid Reserved Words Avoid using MongoDB reserved words (like db, collection, or $ prefixed fields) as field names to prevent conflicts.
code: const userSchema = new mongoose.Schema({ firstName: String, lastName: String, email: { type: String, required: true } });
3.1 Query Efficiency Always query on indexed fields to improve performance. Use projections to return only the fields needed by your application (e.g., { firstName: 1, lastName: 1 }).
3.2 Avoid Full Collection Scans Ensure that all queries can leverage indexes. Use explain() to check the query execution plan and avoid queries that result in full collection scans.
3.3 Bulk Operations Use bulk operations (e.g., bulkWrite) when performing multiple updates, inserts, or deletes to optimize performance and reduce network overhead.
3.4 Use Atomic Operations Prefer atomic update operators (e.g., $set, $inc, $push, $pull) rather than overwriting documents to avoid concurrency issues.
3.5 Pagination Use efficient pagination techniques, avoiding large skip values in queries. Prefer range-based pagination using indexed fields.
4.1 Create Indexes for Query Performance Ensure indexes are created for fields that are frequently queried. Use compound indexes for queries that involve multiple fields.
4.2 TTL Indexes Use TTL indexes (Time-To-Live) for documents that expire after a certain period (e.g., sessions, logs).
4.3 Unique Indexes Enforce unique indexes where necessary, for example on fields like email in a users collection.
code: userSchema.index({ email: 1 }, { unique: true });
5.1 Authentication Always use authentication with usernames and passwords. Use role-based access control (RBAC) to restrict access to certain collections or operations.
5.2 Input Validation Sanitize user inputs to prevent NoSQL injection attacks. Never directly pass user inputs into queries without validation or sanitization.
5.3 Use SSL/TLS Enable SSL/TLS to encrypt data in transit between the application and MongoDB server.
5.4 Field-Level Encryption For sensitive data, use field-level encryption to ensure that only authorized applications can decrypt sensitive fields like passwords or financial information.
5.5 Limit Database Permissions Follow the principle of least privilege. Only grant users the minimum permissions needed to perform their job.
code: const mongoose = require('mongoose'); mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true, });
6.1 Minimize Round-Trips Combine multiple operations into one request whenever possible, such as using bulkWrite() for multiple inserts, updates, or deletes.
6.2 Sharding For large datasets, implement sharding to distribute data across multiple servers. Choose a shard key wisely, one that evenly distributes data and balances the query load.
6.3 Use Connection Pooling Use a connection pool to reuse MongoDB connections rather than creating and destroying connections with every request. Ensure optimal connection pool sizes based on traffic.
code: mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true, poolSize: 10 // Connection Pooling });
7.1 Error Handling Always handle database connection errors, query errors, and timeouts with proper try/catch blocks or error-handling mechanisms in the language of choice.
7.2 Graceful Failures Ensure that your application handles MongoDB failures (e.g., server unavailability, connection time-outs) gracefully by implementing retries or fallbacks.
7.3 Logging Log important database interactions and errors but avoid logging sensitive information like passwords or personal data.
code: mongoose.connection.on('error', (err) => { console.error('MongoDB connection error:', err); process.exit(1); });
8.1 Unit Testing Write unit tests for MongoDB interactions, using mock databases or in-memory databases (such as MongoMemoryServer in Node.js) for faster test execution.
8.2 Query Explain Use explain() to analyze query execution plans and identify inefficient queries that can lead to performance bottlenecks.
code: npm install mongodb-memory-server
9.1 Schema Versioning Track schema changes by including a schemaVersion field or using a dedicated migration service.
9.2 Database Migrations Use a structured migration process (with tools like Migrate-mongo or MongoBee) to handle changes to the schema between different versions of your application.
By adhering to these coding standards, we ensure consistency, maintainability, and scalability across the project. These practices follow industry-level guidelines for React, Express, and MongoDB development.