feat: add comprehensive NodeJS development skills and rules

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
This commit is contained in:
¨NW¨
2026-04-05 02:39:06 +01:00
parent fbc1f6122f
commit 8fcd8f8a9b
10 changed files with 3318 additions and 1 deletions

View File

@@ -0,0 +1,377 @@
# NodeJS Middleware Patterns
Comprehensive middleware patterns for Express.js applications.
## Middleware Types
### 1. Application-level Middleware
```javascript
// Global middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(helmet());
app.use(cors());
// Conditional middleware
app.use((req, res, next) => {
if (req.path.startsWith('/api')) {
return apiLimiter(req, res, next);
}
next();
});
```
### 2. Router-level Middleware
```javascript
const router = require('express').Router();
// Router-specific middleware
router.use(authenticate);
router.use(validateRequest);
// Route-specific middleware
router.post('/', validateUser, controller.create);
router.put('/:id', validateUser, authenticate, controller.update);
```
### 3. Error-handling Middleware
```javascript
// Must have 4 parameters
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.statusCode || 500).json({
status: 'error',
message: err.message
});
});
```
### 4. Built-in Middleware
```javascript
app.use(express.static('public'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
```
## Common Patterns
### Authentication Middleware
```javascript
function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ message: 'Unauthorized' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = verifyToken(token);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ message: 'Invalid token' });
}
}
```
### Authorization Middleware
```javascript
function authorize(...roles) {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({ message: 'Forbidden' });
}
next();
};
}
// Usage
router.delete('/:id', authenticate, authorize('admin'), controller.delete);
```
### Rate Limiting Middleware
```javascript
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
standardHeaders: true,
legacyHeaders: false,
message: { message: 'Too many requests' }
});
app.use('/api', limiter);
```
### Validation Middleware
```javascript
const { validationResult, body } = require('express-validator');
function validate(validations) {
return async (req, res, next) => {
await Promise.all(validations.map(v => v.run(req)));
const errors = validationResult(req);
if (errors.isEmpty()) return next();
res.status(400).json({
status: 'fail',
errors: errors.array().map(err => ({
field: err.path,
message: err.msg
}))
});
};
}
// Usage
router.post('/users',
validate([
body('email').isEmail(),
body('password').isLength({ min: 8 })
]),
controller.create
);
```
### Request Logging Middleware
```javascript
const pino = require('pino');
const logger = pino();
function requestLogger(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
logger.info({
method: req.method,
path: req.path,
status: res.statusCode,
duration,
user: req.user?.id
});
});
next();
}
```
### Context Middleware
```javascript
// Request-scoped context
const asyncLocalStorage = new (require('async_hooks').AsyncLocalStorage)();
function contextMiddleware(req, res, next) {
const context = {
requestId: require('uuid').v4(),
userId: req.user?.id,
startTime: Date.now()
};
asyncLocalStorage.run(context, next);
}
// Access context anywhere in the request lifecycle
function getContext() {
return asyncLocalStorage.getStore();
}
```
### Error Handler Middleware
```javascript
function errorHandler(err, req, res, next) {
const statusCode = err.statusCode || 500;
const status = err.status || 'error';
if (process.env.NODE_ENV === 'development') {
return res.status(statusCode).json({
status,
message: err.message,
stack: err.stack
});
}
if (err.isOperational) {
return res.status(statusCode).json({
status,
message: err.message
});
}
res.status(500).json({
status: 'error',
message: 'Something went wrong'
});
}
```
## Third-party Middleware
### Security (Helmet)
```javascript
const helmet = require('helmet');
app.use(helmet());
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"]
}
}));
```
### Compression
```javascript
const compression = require('compression');
app.use(compression({
threshold: 1024, // Only compress >1KB
filter: (req, res) => {
if (req.headers['x-no-compression']) return false;
return compression.filter(req, res);
}
}));
```
### CORS
```javascript
const cors = require('cors');
app.use(cors({
origin: process.env.CORS_ORIGIN?.split(',') || '*',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
}));
```
### Morgan (Logging)
```javascript
const morgan = require('morgan');
app.use(morgan('dev')); // Development
app.use(morgan('combined')); // Production
```
## Custom Middleware Factory
```javascript
// Custom middleware factory
function createMiddleware(options) {
return (req, res, next) => {
// Pre-processing
if (options.preCondition && !options.preCondition(req)) {
return res.status(400).json({ message: 'Precondition failed' });
}
// Modify request
req.custom = options.transform?.(req) || req.custom;
// Post-processing (on response)
res.on('finish', () => {
options.postProcess?.(req, res);
});
next();
};
}
// Usage
app.use(createMiddleware({
preCondition: (req) => req.headers['x-api-key'],
transform: (req) => ({ ip: req.ip }),
postProcess: (req, res) => console.log(`Request completed: ${req.path}`)
}));
```
## Async Middleware Wrapper
```javascript
function asyncHandler(fn) {
return (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
}
// Wrap middleware
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
res.json({ user });
}));
```
## Middleware Composition
```javascript
// Compose multiple middleware
function compose(...middlewares) {
return (req, res, next) => {
let index = -1;
function dispatch(i) {
if (i <= index) return next(new Error('next() called multiple times'));
index = i;
const middleware = middlewares[i];
if (!middleware) return next();
return middleware(req, res, (err) => {
if (err) return next(err);
return dispatch(i + 1);
});
}
return dispatch(0);
};
}
// Usage
app.use(compose(
authenticate,
authorize('user'),
validateRequest
));
```
## Best Practices
1. **Order matters** - Put error handlers last
2. **Use router middleware** - For route groups
3. **Use async handlers** - Wrap async functions
4. **Keep it simple** - One responsibility per middleware
5. **Use third-party** - Don't reinvent the wheel
6. **Document dependencies** - Comment required fields
## Middleware Order
```javascript
// Correct order
app.use(helmet()); // Security headers
app.use(cors()); // CORS
app.use(compression()); // Compression
app.use(express.json()); // Body parsing
app.use(express.urlencoded()); // URL encoding
app.use(morgan('dev')); // Logging
app.use('/api', apiLimiter); // Rate limiting
app.use('/api', routes); // Routes
app.use(notFoundHandler); // 404 handler
app.use(errorHandler); // Error handler
```