Based on Planner and Memory Manager analysis: New Skills (8): - nodejs-express-patterns: App structure, routing, middleware - nodejs-security-owasp: OWASP Top 10 security practices - nodejs-testing-jest: Unit/integration tests, mocking - nodejs-auth-jwt: JWT authentication, OAuth, sessions - nodejs-error-handling: Error classes, middleware, async handlers - nodejs-middleware-patterns: Auth, validation, rate limiting - nodejs-db-patterns: SQLite, PostgreSQL, MongoDB patterns - nodejs-npm-management: package.json, scripts, dependencies New Rules: - nodejs.md: Code style, security, best practices Updated: - backend-developer.md: Added skills reference table Milestone: #48 NodeJS Development Coverage Related: Planner & Memory Manager analysis results
271 lines
5.5 KiB
Markdown
271 lines
5.5 KiB
Markdown
# NodeJS Development Rules
|
|
|
|
Essential rules for Node.js backend development.
|
|
|
|
## Code Style
|
|
|
|
- Use `const` and `let`, never `var`
|
|
- Use arrow functions for callbacks
|
|
- Use async/await instead of callbacks
|
|
- Use template literals for string interpolation
|
|
- Use object destructuring
|
|
- Use spread operator for objects/arrays
|
|
|
|
```javascript
|
|
// ✅ Good
|
|
const { id, name } = req.body;
|
|
const user = { ...req.body, createdAt: new Date() };
|
|
const users = await User.findAll();
|
|
|
|
// ❌ Bad
|
|
var id = req.body.id;
|
|
const user = Object.assign({}, req.body, { createdAt: new Date() });
|
|
User.findAll().then(users => {});
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
- Always use try/catch with async/await
|
|
- Use centralized error handling middleware
|
|
- Never catch and swallow errors
|
|
- Use custom AppError classes
|
|
- Log errors with context
|
|
|
|
```javascript
|
|
// ✅ Good
|
|
try {
|
|
const user = await User.findById(id);
|
|
if (!user) throw new NotFoundError('User');
|
|
res.json({ user });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
|
|
// ❌ Bad
|
|
User.findById(id).then(user => {
|
|
if (!user) return res.status(404).json({ error: 'Not found' });
|
|
res.json({ user });
|
|
}).catch(err => {}); // Swallowing error
|
|
```
|
|
|
|
## Async Code
|
|
|
|
- Always use async/await
|
|
- Never mix callbacks and promises
|
|
- Use Promise.all for parallel operations
|
|
- Use async middleware wrapper
|
|
|
|
```javascript
|
|
// ✅ Good
|
|
const [users, posts] = await Promise.all([
|
|
User.findAll(),
|
|
Post.findAll()
|
|
]);
|
|
|
|
// ❌ Bad
|
|
let users;
|
|
User.findAll().then(u => { users = u; });
|
|
console.log(users); // undefined
|
|
```
|
|
|
|
## Security
|
|
|
|
- Always validate and sanitize input
|
|
- Use parameterized queries
|
|
- Never expose sensitive data
|
|
- Use HTTPS in production
|
|
- Set security headers with helmet
|
|
- Rate limit public endpoints
|
|
|
|
```javascript
|
|
// ✅ Good
|
|
const user = await db.query('SELECT * FROM users WHERE id = ?', [id]);
|
|
app.use(helmet());
|
|
|
|
// ❌ Bad
|
|
const user = await db.query(`SELECT * FROM users WHERE id = ${id}`);
|
|
// SQL injection vulnerable
|
|
```
|
|
|
|
## Authentication
|
|
|
|
- Never store passwords in plain text
|
|
- Use bcrypt for password hashing
|
|
- Use short-lived access tokens
|
|
- Use refresh tokens
|
|
- Use httpOnly cookies
|
|
- Never put secrets in JWT payload
|
|
|
|
```javascript
|
|
// ✅ Good
|
|
const hashedPassword = await bcrypt.hash(password, 12);
|
|
const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' });
|
|
|
|
// ❌ Bad
|
|
const hashedPassword = password; // No hash
|
|
const token = jwt.sign({ password: user.password }, 'secret'); // Secret in payload
|
|
```
|
|
|
|
## Express Best Practices
|
|
|
|
- Use express.Router() for route organization
|
|
- Keep route handlers thin
|
|
- Validate at route level
|
|
- Put error handlers last
|
|
- Use middleware for cross-cutting concerns
|
|
|
|
```javascript
|
|
// ✅ Good
|
|
// routes/users.js
|
|
const router = express.Router();
|
|
router.get('/', authenticate, validate, controller.list);
|
|
|
|
// app.js
|
|
app.use('/api/users', routes.users);
|
|
app.use(errorHandler); // Last middleware
|
|
|
|
// ❌ Bad
|
|
app.get('/api/users', async (req, res) => {
|
|
// All logic in route
|
|
});
|
|
```
|
|
|
|
## Database
|
|
|
|
- Use connection pooling
|
|
- Close connections gracefully
|
|
- Use transactions for writes
|
|
- Index frequently queried fields
|
|
- Use migrations for schema changes
|
|
|
|
```javascript
|
|
// ✅ Good
|
|
await db.transaction(async (trx) => {
|
|
await trx('users').insert(user);
|
|
await trx('profiles').insert(profile);
|
|
});
|
|
|
|
// ❌ Bad
|
|
async function createUser(data) {
|
|
const user = await db('users').insert(data);
|
|
// No transaction, partial data on error
|
|
await Profile.create({ userId: user.id });
|
|
}
|
|
```
|
|
|
|
## Logging
|
|
|
|
- Use structured logging (pino, winston)
|
|
- Log levels: error, warn, info, debug
|
|
- Include request ID for tracing
|
|
- Log errors with stack traces
|
|
- Don't log sensitive data
|
|
|
|
```javascript
|
|
// ✅ Good
|
|
logger.info({ userId, action: 'login', ip: req.ip });
|
|
|
|
// ❌ Bad
|
|
console.log('User logged in:', user); // Logs entire user including password
|
|
```
|
|
|
|
## Testing
|
|
|
|
- Write tests for critical paths
|
|
- Use Jest or Mocha
|
|
- Mock external dependencies
|
|
- Aim for 80%+ coverage
|
|
- Test edge cases
|
|
|
|
```javascript
|
|
// ✅ Good
|
|
describe('UserService', () => {
|
|
it('should create user with hashed password', async () => {
|
|
const user = await service.create({ email, password });
|
|
expect(user.password).not.toBe(password);
|
|
});
|
|
});
|
|
```
|
|
|
|
## Environment
|
|
|
|
- Use .env for secrets
|
|
- Never commit secrets
|
|
- Use different configs for environments
|
|
- Validate required env vars
|
|
|
|
```javascript
|
|
// ✅ Good
|
|
const config = {
|
|
db: {
|
|
url: process.env.DATABASE_URL
|
|
}
|
|
};
|
|
|
|
if (!config.db.url) {
|
|
throw new Error('DATABASE_URL is required');
|
|
}
|
|
|
|
// ❌ Bad
|
|
const config = {
|
|
db: {
|
|
url: 'postgres://user:pass@localhost/db' // Hardcoded
|
|
}
|
|
};
|
|
```
|
|
|
|
## Package Management
|
|
|
|
- Use exact versions in production
|
|
- Run npm audit regularly
|
|
- Update dependencies regularly
|
|
- Remove unused dependencies
|
|
|
|
```bash
|
|
# ✅ Good
|
|
npm audit
|
|
npx depcheck
|
|
|
|
# ❌ Bad
|
|
# Never running security audit
|
|
# Many unused dependencies
|
|
```
|
|
|
|
## Performance
|
|
|
|
- Use streaming for large files
|
|
- Cache frequently accessed data
|
|
- Use connection pooling
|
|
- Implement pagination
|
|
- Compress responses
|
|
|
|
```javascript
|
|
// ✅ Good
|
|
app.use(compression());
|
|
app.get('/users', paginated, controller.list);
|
|
|
|
// ❌ Bad
|
|
app.get('/users', async (req, res) => {
|
|
const users = await User.findAll(); // All users at once
|
|
res.json(users);
|
|
});
|
|
```
|
|
|
|
## Clean Code
|
|
|
|
- No magic numbers, use constants
|
|
- Meaningful variable names
|
|
- One function, one responsibility
|
|
- Comments only for "why", not "what"
|
|
- DRY principle
|
|
|
|
```javascript
|
|
// ✅ Good
|
|
const MAX_LOGIN_ATTEMPTS = 5;
|
|
const isLocked = user.loginAttempts >= MAX_LOGIN_ATTEMPTS;
|
|
|
|
// ❌ Bad
|
|
if (user.loginAttempts >= 5) { // Magic number
|
|
// ...
|
|
}
|
|
``` |