claude-mpm 4.17.0__py3-none-any.whl → 4.18.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (47) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_PM.md +48 -17
  3. claude_mpm/agents/agent_loader.py +4 -4
  4. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  5. claude_mpm/config/agent_config.py +2 -2
  6. claude_mpm/core/factories.py +1 -1
  7. claude_mpm/core/optimized_agent_loader.py +3 -3
  8. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  9. claude_mpm/models/resume_log.py +340 -0
  10. claude_mpm/services/agents/auto_config_manager.py +1 -1
  11. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  12. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  13. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  14. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  15. claude_mpm/services/agents/local_template_manager.py +1 -1
  16. claude_mpm/services/core/path_resolver.py +1 -1
  17. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  18. claude_mpm/services/mcp_config_manager.py +2 -2
  19. claude_mpm/services/session_manager.py +205 -1
  20. claude_mpm/services/unified/deployment_strategies/local.py +1 -1
  21. claude_mpm/skills/bundled/api-documentation.md +393 -0
  22. claude_mpm/skills/bundled/async-testing.md +571 -0
  23. claude_mpm/skills/bundled/code-review.md +143 -0
  24. claude_mpm/skills/bundled/database-migration.md +199 -0
  25. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  26. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  27. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  28. claude_mpm/skills/bundled/git-workflow.md +414 -0
  29. claude_mpm/skills/bundled/imagemagick.md +204 -0
  30. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  31. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  32. claude_mpm/skills/bundled/pdf.md +141 -0
  33. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  34. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  35. claude_mpm/skills/bundled/security-scanning.md +327 -0
  36. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  37. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  38. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  39. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  40. claude_mpm/skills/bundled/xlsx.md +157 -0
  41. claude_mpm/utils/agent_dependency_loader.py +2 -2
  42. {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/METADATA +68 -1
  43. {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/RECORD +47 -24
  44. {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/WHEEL +0 -0
  45. {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/entry_points.txt +0 -0
  46. {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/licenses/LICENSE +0 -0
  47. {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1429 @@
1
+ ---
2
+ skill_id: express-local-dev
3
+ skill_version: 0.1.0
4
+ description: Running Express development servers with auto-reload tools like Nodemon, managing production deployments with PM2 clustering, and implementing graceful shutdown patterns.
5
+ updated_at: 2025-10-30T17:00:00Z
6
+ tags: [express, nodejs, development, server, backend]
7
+ ---
8
+
9
+ # Express Local Development Server
10
+
11
+ ## Overview
12
+
13
+ Express is a minimal and flexible Node.js web application framework providing a robust set of features for web and mobile applications. This skill covers running Express development servers with auto-reload tools like Nodemon, managing production deployments with PM2 clustering, and implementing graceful shutdown patterns.
14
+
15
+ ## When to Use This Skill
16
+
17
+ - Setting up Express development environment with auto-reload
18
+ - Configuring Nodemon for optimal development workflow
19
+ - Troubleshooting file watching and reload issues
20
+ - Managing Express production deployment with PM2
21
+ - Implementing graceful shutdown handlers
22
+ - Configuring zero-downtime reloads
23
+ - Coordinating multiple Express instances
24
+ - Understanding Nodemon vs PM2 trade-offs
25
+
26
+ ## Quick Start
27
+
28
+ ### Development Server
29
+
30
+ **Basic Express Server:**
31
+ ```javascript
32
+ // server.js
33
+ const express = require('express');
34
+ const app = express();
35
+ const PORT = process.env.PORT || 3000;
36
+
37
+ app.get('/', (req, res) => {
38
+ res.json({ message: 'Hello World' });
39
+ });
40
+
41
+ const server = app.listen(PORT, () => {
42
+ console.log(`Server running on port ${PORT}`);
43
+ });
44
+
45
+ // Graceful shutdown
46
+ process.on('SIGTERM', () => {
47
+ console.log('SIGTERM received, closing server...');
48
+ server.close(() => {
49
+ console.log('Server closed');
50
+ process.exit(0);
51
+ });
52
+ });
53
+ ```
54
+
55
+ **With Nodemon:**
56
+ ```bash
57
+ # Install nodemon
58
+ npm install -D nodemon
59
+
60
+ # Run with nodemon
61
+ npx nodemon server.js
62
+
63
+ # Or add to package.json
64
+ npm run dev
65
+ ```
66
+
67
+ **With TypeScript:**
68
+ ```bash
69
+ # Install dependencies
70
+ npm install -D typescript @types/express @types/node ts-node nodemon
71
+
72
+ # Run with nodemon + ts-node
73
+ npx nodemon --exec ts-node src/server.ts
74
+ ```
75
+
76
+ ### With PM2 (Production)
77
+
78
+ **Basic PM2 Start:**
79
+ ```bash
80
+ # Start Express with PM2
81
+ pm2 start server.js --name "express-app"
82
+
83
+ # With environment variables
84
+ pm2 start server.js --name "express-app" --env production
85
+
86
+ # Cluster mode (multiple instances)
87
+ pm2 start server.js -i max
88
+ ```
89
+
90
+ **Watch Mode (Use Carefully):**
91
+ ```bash
92
+ # PM2 with watch (development only, not recommended)
93
+ pm2 start server.js --watch --ignore-watch="node_modules"
94
+ ```
95
+
96
+ ### With Docker
97
+
98
+ **Development Dockerfile:**
99
+ ```dockerfile
100
+ FROM node:18-alpine
101
+
102
+ WORKDIR /app
103
+
104
+ COPY package*.json ./
105
+ RUN npm install
106
+
107
+ COPY . .
108
+
109
+ EXPOSE 3000
110
+
111
+ CMD ["npm", "run", "dev"]
112
+ ```
113
+
114
+ **Production Dockerfile:**
115
+ ```dockerfile
116
+ FROM node:18-alpine
117
+
118
+ WORKDIR /app
119
+
120
+ COPY package*.json ./
121
+ RUN npm ci --only=production
122
+
123
+ COPY . .
124
+
125
+ EXPOSE 3000
126
+
127
+ CMD ["node", "server.js"]
128
+ ```
129
+
130
+ **Docker Compose:**
131
+ ```yaml
132
+ version: '3.8'
133
+
134
+ services:
135
+ express:
136
+ build: .
137
+ ports:
138
+ - "3000:3000"
139
+ volumes:
140
+ - .:/app
141
+ - /app/node_modules
142
+ environment:
143
+ - NODE_ENV=development
144
+ - PORT=3000
145
+ command: npm run dev
146
+
147
+ redis:
148
+ image: redis:7-alpine
149
+ ports:
150
+ - "6379:6379"
151
+ ```
152
+
153
+ ## Configuration Patterns
154
+
155
+ ### Nodemon Configuration
156
+
157
+ **nodemon.json:**
158
+ ```json
159
+ {
160
+ "watch": ["src", "config"],
161
+ "ext": "js,json,ts",
162
+ "ignore": ["src/**/*.test.js", "node_modules"],
163
+ "exec": "node server.js",
164
+ "env": {
165
+ "NODE_ENV": "development"
166
+ },
167
+ "delay": 1000,
168
+ "verbose": true,
169
+ "restartable": "rs"
170
+ }
171
+ ```
172
+
173
+ **Package.json Scripts:**
174
+ ```json
175
+ {
176
+ "scripts": {
177
+ "dev": "nodemon server.js",
178
+ "dev:debug": "nodemon --inspect server.js",
179
+ "dev:ts": "nodemon --exec ts-node src/server.ts",
180
+ "start": "node server.js",
181
+ "start:prod": "NODE_ENV=production node server.js"
182
+ },
183
+ "devDependencies": {
184
+ "nodemon": "^3.0.1"
185
+ }
186
+ }
187
+ ```
188
+
189
+ **Advanced Nodemon Configuration:**
190
+ ```json
191
+ {
192
+ "watch": ["src"],
193
+ "ext": "js,json,graphql",
194
+ "ignore": [
195
+ "src/**/*.test.js",
196
+ "src/**/*.spec.js",
197
+ "node_modules/**/*",
198
+ "dist/**/*"
199
+ ],
200
+ "exec": "node --trace-warnings server.js",
201
+ "env": {
202
+ "NODE_ENV": "development",
203
+ "DEBUG": "express:*"
204
+ },
205
+ "events": {
206
+ "restart": "echo 'App restarted due to file change'"
207
+ },
208
+ "delay": 2000,
209
+ "signal": "SIGTERM",
210
+ "verbose": false
211
+ }
212
+ ```
213
+
214
+ ### PM2 Clustering for Production
215
+
216
+ **Ecosystem Configuration:**
217
+ ```javascript
218
+ // ecosystem.config.js
219
+ module.exports = {
220
+ apps: [{
221
+ name: 'express-app',
222
+ script: './server.js',
223
+ instances: 'max', // Use all CPU cores
224
+ exec_mode: 'cluster',
225
+
226
+ // Environment variables
227
+ env: {
228
+ NODE_ENV: 'development',
229
+ PORT: 3000
230
+ },
231
+ env_production: {
232
+ NODE_ENV: 'production',
233
+ PORT: 8080
234
+ },
235
+
236
+ // Restart policies
237
+ autorestart: true,
238
+ max_restarts: 10,
239
+ min_uptime: '10s',
240
+ max_memory_restart: '500M',
241
+
242
+ // Watch mode (development only, use nodemon instead)
243
+ watch: false,
244
+
245
+ // Logging
246
+ error_file: './logs/pm2-error.log',
247
+ out_file: './logs/pm2-out.log',
248
+ log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
249
+ merge_logs: true,
250
+
251
+ // Graceful shutdown
252
+ kill_timeout: 5000,
253
+ wait_ready: true,
254
+ listen_timeout: 10000,
255
+ }]
256
+ };
257
+ ```
258
+
259
+ **Start PM2 Cluster:**
260
+ ```bash
261
+ # Start cluster with config
262
+ pm2 start ecosystem.config.js --env production
263
+
264
+ # Monitor cluster
265
+ pm2 monit
266
+
267
+ # View cluster status
268
+ pm2 list
269
+
270
+ # Reload without downtime
271
+ pm2 reload express-app
272
+ ```
273
+
274
+ **Manual Clustering Configuration:**
275
+ ```bash
276
+ # Start with 4 instances
277
+ pm2 start server.js -i 4
278
+
279
+ # Use all CPU cores
280
+ pm2 start server.js -i max
281
+
282
+ # Scale up/down
283
+ pm2 scale express-app 8
284
+ pm2 scale express-app +2
285
+ ```
286
+
287
+ ### Nodemon vs PM2 Comparison
288
+
289
+ **Nodemon (Development Recommended):**
290
+
291
+ Pros:
292
+ - Designed for development
293
+ - Fast restarts on file changes
294
+ - Simple configuration
295
+ - Better developer experience
296
+ - Automatic detection of file changes
297
+ - No cluster complexity in dev
298
+
299
+ Cons:
300
+ - Single instance only
301
+ - Not for production
302
+ - No advanced process management
303
+
304
+ **Use Nodemon when:**
305
+ - Developing locally
306
+ - Need fast feedback on changes
307
+ - Testing and debugging
308
+ - Single developer workflow
309
+
310
+ **PM2 (Production Recommended):**
311
+
312
+ Pros:
313
+ - Multi-instance clustering
314
+ - Zero-downtime reload
315
+ - Process monitoring
316
+ - Automatic restarts
317
+ - Log management
318
+ - Load balancing
319
+ - Production-grade features
320
+
321
+ Cons:
322
+ - Watch mode can be unreliable
323
+ - More complex configuration
324
+ - Slower restarts than Nodemon
325
+ - Overkill for development
326
+
327
+ **Use PM2 when:**
328
+ - Production deployment
329
+ - Need multiple instances
330
+ - Zero-downtime requirements
331
+ - System-level process management
332
+ - Advanced monitoring needed
333
+
334
+ **Recommendation:**
335
+ - Development: Use Nodemon
336
+ - Production: Use PM2 (no watch mode)
337
+
338
+ ### Graceful Shutdown Implementation
339
+
340
+ **Basic Graceful Shutdown:**
341
+ ```javascript
342
+ const express = require('express');
343
+ const app = express();
344
+ const PORT = process.env.PORT || 3000;
345
+
346
+ app.get('/', (req, res) => {
347
+ res.json({ status: 'ok' });
348
+ });
349
+
350
+ const server = app.listen(PORT, () => {
351
+ console.log(`Server running on port ${PORT}`);
352
+ });
353
+
354
+ // Graceful shutdown handler
355
+ function gracefulShutdown(signal) {
356
+ console.log(`${signal} received, starting graceful shutdown`);
357
+
358
+ server.close(() => {
359
+ console.log('HTTP server closed');
360
+
361
+ // Close database connections
362
+ // db.close();
363
+
364
+ // Close other resources
365
+ // redis.quit();
366
+
367
+ console.log('All connections closed, exiting');
368
+ process.exit(0);
369
+ });
370
+
371
+ // Force exit after timeout
372
+ setTimeout(() => {
373
+ console.error('Forcing shutdown after timeout');
374
+ process.exit(1);
375
+ }, 10000);
376
+ }
377
+
378
+ // Listen for termination signals
379
+ process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
380
+ process.on('SIGINT', () => gracefulShutdown('SIGINT'));
381
+ ```
382
+
383
+ **Advanced Graceful Shutdown with Cleanup:**
384
+ ```javascript
385
+ const express = require('express');
386
+ const mongoose = require('mongoose');
387
+ const redis = require('redis');
388
+
389
+ const app = express();
390
+ const PORT = process.env.PORT || 3000;
391
+
392
+ // Setup
393
+ const redisClient = redis.createClient();
394
+ mongoose.connect(process.env.MONGODB_URI);
395
+
396
+ let isShuttingDown = false;
397
+
398
+ // Health check endpoint
399
+ app.get('/health', (req, res) => {
400
+ if (isShuttingDown) {
401
+ res.status(503).json({ status: 'shutting down' });
402
+ } else {
403
+ res.json({ status: 'ok' });
404
+ }
405
+ });
406
+
407
+ const server = app.listen(PORT, () => {
408
+ console.log(`Server running on port ${PORT}`);
409
+
410
+ // Signal to PM2 that app is ready
411
+ if (process.send) {
412
+ process.send('ready');
413
+ }
414
+ });
415
+
416
+ // Graceful shutdown
417
+ async function gracefulShutdown(signal) {
418
+ if (isShuttingDown) return;
419
+
420
+ console.log(`${signal} received, starting graceful shutdown`);
421
+ isShuttingDown = true;
422
+
423
+ // Stop accepting new connections
424
+ server.close(async () => {
425
+ console.log('HTTP server closed');
426
+
427
+ try {
428
+ // Close database connections
429
+ await mongoose.connection.close();
430
+ console.log('MongoDB connection closed');
431
+
432
+ // Close Redis connection
433
+ await redisClient.quit();
434
+ console.log('Redis connection closed');
435
+
436
+ console.log('Graceful shutdown completed');
437
+ process.exit(0);
438
+ } catch (error) {
439
+ console.error('Error during shutdown:', error);
440
+ process.exit(1);
441
+ }
442
+ });
443
+
444
+ // Force exit after 30 seconds
445
+ setTimeout(() => {
446
+ console.error('Forcing shutdown after timeout');
447
+ process.exit(1);
448
+ }, 30000);
449
+ }
450
+
451
+ process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
452
+ process.on('SIGINT', () => gracefulShutdown('SIGINT'));
453
+
454
+ // Handle uncaught errors
455
+ process.on('uncaughtException', (error) => {
456
+ console.error('Uncaught exception:', error);
457
+ gracefulShutdown('uncaughtException');
458
+ });
459
+
460
+ process.on('unhandledRejection', (reason, promise) => {
461
+ console.error('Unhandled rejection at:', promise, 'reason:', reason);
462
+ gracefulShutdown('unhandledRejection');
463
+ });
464
+ ```
465
+
466
+ **PM2 Integration:**
467
+ ```javascript
468
+ // Send ready signal to PM2
469
+ server.listen(PORT, () => {
470
+ console.log(`Server listening on port ${PORT}`);
471
+
472
+ if (process.send) {
473
+ process.send('ready');
474
+ }
475
+ });
476
+
477
+ // Handle PM2 shutdown message
478
+ process.on('message', (msg) => {
479
+ if (msg === 'shutdown') {
480
+ gracefulShutdown('PM2 shutdown message');
481
+ }
482
+ });
483
+ ```
484
+
485
+ ### Zero-Downtime Reload
486
+
487
+ **PM2 Reload (Graceful):**
488
+ ```bash
489
+ # Reload all instances gracefully
490
+ pm2 reload express-app
491
+
492
+ # Reload with delay between instances
493
+ pm2 reload express-app --update-env
494
+
495
+ # Force restart (not graceful)
496
+ pm2 restart express-app
497
+ ```
498
+
499
+ **Ecosystem Config for Zero-Downtime:**
500
+ ```javascript
501
+ module.exports = {
502
+ apps: [{
503
+ name: 'express-app',
504
+ script: './server.js',
505
+ instances: 4,
506
+ exec_mode: 'cluster',
507
+
508
+ // Zero-downtime reload settings
509
+ wait_ready: true, // Wait for ready signal
510
+ listen_timeout: 10000, // Timeout for ready signal
511
+ kill_timeout: 5000, // Time to wait for graceful shutdown
512
+
513
+ // Restart behavior
514
+ autorestart: true,
515
+ max_restarts: 10,
516
+ min_uptime: '10s',
517
+ }]
518
+ };
519
+ ```
520
+
521
+ **Application Code for Zero-Downtime:**
522
+ ```javascript
523
+ const express = require('express');
524
+ const app = express();
525
+ const PORT = process.env.PORT || 3000;
526
+
527
+ const server = app.listen(PORT, () => {
528
+ console.log(`Server running on port ${PORT}`);
529
+
530
+ // Signal PM2 that app is ready
531
+ if (process.send) {
532
+ process.send('ready');
533
+ }
534
+ });
535
+
536
+ // Graceful shutdown for PM2 reload
537
+ process.on('SIGINT', () => {
538
+ console.log('SIGINT received, closing server');
539
+
540
+ server.close(() => {
541
+ console.log('Server closed');
542
+ process.exit(0);
543
+ });
544
+
545
+ // Timeout fallback
546
+ setTimeout(() => process.exit(1), 10000);
547
+ });
548
+ ```
549
+
550
+ **Deployment Process:**
551
+ ```bash
552
+ # Deploy new code
553
+ git pull origin main
554
+ npm install --production
555
+
556
+ # Reload without downtime
557
+ pm2 reload ecosystem.config.js --env production
558
+
559
+ # Verify instances
560
+ pm2 list
561
+ pm2 logs express-app --lines 50
562
+ ```
563
+
564
+ ### Multiple Instance Coordination
565
+
566
+ **Shared State with Redis:**
567
+ ```javascript
568
+ const express = require('express');
569
+ const redis = require('redis');
570
+ const session = require('express-session');
571
+ const RedisStore = require('connect-redis').default;
572
+
573
+ const app = express();
574
+
575
+ // Redis client
576
+ const redisClient = redis.createClient({
577
+ host: process.env.REDIS_HOST || 'localhost',
578
+ port: process.env.REDIS_PORT || 6379,
579
+ });
580
+
581
+ redisClient.on('error', (err) => console.error('Redis error:', err));
582
+
583
+ // Session store with Redis
584
+ app.use(session({
585
+ store: new RedisStore({ client: redisClient }),
586
+ secret: process.env.SESSION_SECRET,
587
+ resave: false,
588
+ saveUninitialized: false,
589
+ cookie: {
590
+ secure: process.env.NODE_ENV === 'production',
591
+ maxAge: 1000 * 60 * 60 * 24, // 24 hours
592
+ },
593
+ }));
594
+ ```
595
+
596
+ **Cluster-Safe In-Memory Caching:**
597
+ ```javascript
598
+ // DON'T: In-memory cache (not shared across instances)
599
+ const cache = {};
600
+
601
+ app.get('/data/:id', (req, res) => {
602
+ const cached = cache[req.params.id]; // Different per instance!
603
+ if (cached) return res.json(cached);
604
+
605
+ // Fetch and cache
606
+ const data = fetchData(req.params.id);
607
+ cache[req.params.id] = data;
608
+ res.json(data);
609
+ });
610
+
611
+ // DO: Redis cache (shared across instances)
612
+ app.get('/data/:id', async (req, res) => {
613
+ const cached = await redisClient.get(`data:${req.params.id}`);
614
+ if (cached) return res.json(JSON.parse(cached));
615
+
616
+ // Fetch and cache in Redis
617
+ const data = await fetchData(req.params.id);
618
+ await redisClient.setEx(`data:${req.params.id}`, 3600, JSON.stringify(data));
619
+ res.json(data);
620
+ });
621
+ ```
622
+
623
+ **Worker Coordination:**
624
+ ```javascript
625
+ // Use Redis for distributed locks
626
+ const Redlock = require('redlock');
627
+
628
+ const redlock = new Redlock([redisClient], {
629
+ retryCount: 10,
630
+ retryDelay: 200,
631
+ });
632
+
633
+ app.post('/process-job', async (req, res) => {
634
+ const lock = await redlock.acquire(['locks:process-job'], 5000);
635
+
636
+ try {
637
+ // Only one instance processes this at a time
638
+ await processJob(req.body);
639
+ res.json({ success: true });
640
+ } finally {
641
+ await lock.release();
642
+ }
643
+ });
644
+ ```
645
+
646
+ **Socket.IO with PM2 Cluster:**
647
+ ```javascript
648
+ const express = require('express');
649
+ const http = require('http');
650
+ const socketIO = require('socket.io');
651
+ const redis = require('redis');
652
+ const { createAdapter } = require('@socket.io/redis-adapter');
653
+
654
+ const app = express();
655
+ const server = http.createServer(app);
656
+ const io = socketIO(server);
657
+
658
+ // Redis adapter for Socket.IO clustering
659
+ const pubClient = redis.createClient();
660
+ const subClient = pubClient.duplicate();
661
+
662
+ io.adapter(createAdapter(pubClient, subClient));
663
+
664
+ io.on('connection', (socket) => {
665
+ console.log('Client connected');
666
+
667
+ socket.on('message', (data) => {
668
+ // Broadcast across all instances
669
+ io.emit('message', data);
670
+ });
671
+ });
672
+
673
+ server.listen(3000);
674
+ ```
675
+
676
+ ## Framework-Specific Best Practices
677
+
678
+ ### Middleware Organization
679
+
680
+ **Proper Middleware Order:**
681
+ ```javascript
682
+ const express = require('express');
683
+ const helmet = require('helmet');
684
+ const cors = require('cors');
685
+ const compression = require('compression');
686
+ const morgan = require('morgan');
687
+
688
+ const app = express();
689
+
690
+ // Security middleware (first)
691
+ app.use(helmet());
692
+ app.use(cors({
693
+ origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
694
+ credentials: true,
695
+ }));
696
+
697
+ // Request parsing
698
+ app.use(express.json({ limit: '10mb' }));
699
+ app.use(express.urlencoded({ extended: true }));
700
+
701
+ // Compression
702
+ app.use(compression());
703
+
704
+ // Logging
705
+ if (process.env.NODE_ENV !== 'production') {
706
+ app.use(morgan('dev'));
707
+ } else {
708
+ app.use(morgan('combined'));
709
+ }
710
+
711
+ // Routes
712
+ app.use('/api/users', require('./routes/users'));
713
+ app.use('/api/posts', require('./routes/posts'));
714
+
715
+ // Error handling (last)
716
+ app.use((err, req, res, next) => {
717
+ console.error(err.stack);
718
+ res.status(500).json({ error: 'Internal server error' });
719
+ });
720
+ ```
721
+
722
+ ### Error Handling Patterns
723
+
724
+ **Centralized Error Handler:**
725
+ ```javascript
726
+ // errors/AppError.js
727
+ class AppError extends Error {
728
+ constructor(message, statusCode) {
729
+ super(message);
730
+ this.statusCode = statusCode;
731
+ this.isOperational = true;
732
+ Error.captureStackTrace(this, this.constructor);
733
+ }
734
+ }
735
+
736
+ // middleware/errorHandler.js
737
+ function errorHandler(err, req, res, next) {
738
+ err.statusCode = err.statusCode || 500;
739
+ err.status = err.status || 'error';
740
+
741
+ if (process.env.NODE_ENV === 'development') {
742
+ res.status(err.statusCode).json({
743
+ status: err.status,
744
+ error: err,
745
+ message: err.message,
746
+ stack: err.stack,
747
+ });
748
+ } else {
749
+ // Production: don't leak error details
750
+ if (err.isOperational) {
751
+ res.status(err.statusCode).json({
752
+ status: err.status,
753
+ message: err.message,
754
+ });
755
+ } else {
756
+ console.error('ERROR:', err);
757
+ res.status(500).json({
758
+ status: 'error',
759
+ message: 'Something went wrong',
760
+ });
761
+ }
762
+ }
763
+ }
764
+
765
+ // server.js
766
+ app.use(errorHandler);
767
+ ```
768
+
769
+ **Async Error Wrapper:**
770
+ ```javascript
771
+ // utils/catchAsync.js
772
+ const catchAsync = (fn) => {
773
+ return (req, res, next) => {
774
+ fn(req, res, next).catch(next);
775
+ };
776
+ };
777
+
778
+ // Usage
779
+ const getUser = catchAsync(async (req, res) => {
780
+ const user = await User.findById(req.params.id);
781
+ if (!user) throw new AppError('User not found', 404);
782
+ res.json({ user });
783
+ });
784
+
785
+ app.get('/users/:id', getUser);
786
+ ```
787
+
788
+ ### Environment Configuration
789
+
790
+ **Configuration Management:**
791
+ ```javascript
792
+ // config/index.js
793
+ require('dotenv').config();
794
+
795
+ module.exports = {
796
+ env: process.env.NODE_ENV || 'development',
797
+ port: parseInt(process.env.PORT, 10) || 3000,
798
+ database: {
799
+ uri: process.env.DATABASE_URL,
800
+ options: {
801
+ useNewUrlParser: true,
802
+ useUnifiedTopology: true,
803
+ },
804
+ },
805
+ redis: {
806
+ host: process.env.REDIS_HOST || 'localhost',
807
+ port: parseInt(process.env.REDIS_PORT, 10) || 6379,
808
+ },
809
+ jwt: {
810
+ secret: process.env.JWT_SECRET,
811
+ expiresIn: process.env.JWT_EXPIRES_IN || '7d',
812
+ },
813
+ cors: {
814
+ origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
815
+ },
816
+ };
817
+ ```
818
+
819
+ ### Logging Best Practices
820
+
821
+ **Structured Logging:**
822
+ ```javascript
823
+ const winston = require('winston');
824
+
825
+ const logger = winston.createLogger({
826
+ level: process.env.LOG_LEVEL || 'info',
827
+ format: winston.format.combine(
828
+ winston.format.timestamp(),
829
+ winston.format.errors({ stack: true }),
830
+ winston.format.json()
831
+ ),
832
+ transports: [
833
+ new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
834
+ new winston.transports.File({ filename: 'logs/combined.log' }),
835
+ ],
836
+ });
837
+
838
+ if (process.env.NODE_ENV !== 'production') {
839
+ logger.add(new winston.transports.Console({
840
+ format: winston.format.simple(),
841
+ }));
842
+ }
843
+
844
+ // Usage
845
+ logger.info('Server started', { port: 3000 });
846
+ logger.error('Database error', { error: err.message });
847
+ ```
848
+
849
+ ## Common Problems & Solutions
850
+
851
+ ### Problem 1: Port Already in Use
852
+
853
+ **Symptoms:**
854
+ ```
855
+ Error: listen EADDRINUSE: address already in use :::3000
856
+ ```
857
+
858
+ **Root Cause:**
859
+ Another process (previous Express instance, other server, or zombie process) is using port 3000.
860
+
861
+ **Solution:**
862
+
863
+ **Option A: Kill Process**
864
+ ```bash
865
+ # macOS/Linux
866
+ lsof -ti:3000 | xargs kill -9
867
+
868
+ # Alternative
869
+ fuser -k 3000/tcp
870
+
871
+ # Windows
872
+ netstat -ano | findstr :3000
873
+ taskkill /PID <PID> /F
874
+ ```
875
+
876
+ **Option B: Use Different Port**
877
+ ```bash
878
+ PORT=3001 npm run dev
879
+ ```
880
+
881
+ **Option C: Dynamic Port Assignment**
882
+ ```javascript
883
+ const express = require('express');
884
+ const app = express();
885
+
886
+ const PORT = process.env.PORT || 3000;
887
+
888
+ const server = app.listen(PORT, () => {
889
+ console.log(`Server running on port ${server.address().port}`);
890
+ });
891
+
892
+ // Or find available port
893
+ const portfinder = require('portfinder');
894
+
895
+ portfinder.getPort((err, port) => {
896
+ if (err) throw err;
897
+ app.listen(port, () => {
898
+ console.log(`Server running on port ${port}`);
899
+ });
900
+ });
901
+ ```
902
+
903
+ **Option D: Cleanup Script**
904
+ ```json
905
+ {
906
+ "scripts": {
907
+ "predev": "kill-port 3000 || true",
908
+ "dev": "nodemon server.js"
909
+ }
910
+ }
911
+ ```
912
+
913
+ ### Problem 2: Nodemon Not Restarting on Changes
914
+
915
+ **Symptoms:**
916
+ - File changes don't trigger restart
917
+ - Need to manually stop and start server
918
+ - Nodemon seems to be running but not watching
919
+
920
+ **Root Cause:**
921
+ Incorrect watch configuration, file permission issues, or unsupported file system (WSL, network drives).
922
+
923
+ **Solution:**
924
+
925
+ **Step 1: Verify Nodemon is Watching**
926
+ ```bash
927
+ # Run with verbose output
928
+ npx nodemon --verbose server.js
929
+ ```
930
+
931
+ **Step 2: Configure Watch Directories**
932
+ ```json
933
+ {
934
+ "watch": ["src", "config"],
935
+ "ext": "js,json",
936
+ "ignore": ["node_modules/**/*", "test/**/*"]
937
+ }
938
+ ```
939
+
940
+ **Step 3: Force Polling (WSL/Network Drives)**
941
+ ```json
942
+ {
943
+ "watch": ["src"],
944
+ "legacyWatch": true,
945
+ "pollingInterval": 1000
946
+ }
947
+ ```
948
+
949
+ **Step 4: Check File Permissions**
950
+ ```bash
951
+ # Ensure files are readable
952
+ chmod -R 644 src/**/*.js
953
+ ```
954
+
955
+ **Step 5: Increase Delay**
956
+ ```json
957
+ {
958
+ "delay": 2000,
959
+ "debounce": 1000
960
+ }
961
+ ```
962
+
963
+ **Step 6: Manual Restart**
964
+ ```bash
965
+ # Type 'rs' and Enter to manually restart
966
+ npx nodemon server.js
967
+ rs
968
+ ```
969
+
970
+ ### Problem 3: PM2 Cluster Instances Crashing
971
+
972
+ **Symptoms:**
973
+ ```
974
+ [PM2][ERROR] App crashed
975
+ PM2 | App [express-app:0] exited with code [1]
976
+ ```
977
+
978
+ **Root Cause:**
979
+ Unhandled errors, memory leaks, or improper graceful shutdown in cluster mode.
980
+
981
+ **Solution:**
982
+
983
+ **Step 1: Check Logs**
984
+ ```bash
985
+ pm2 logs express-app --lines 100
986
+ pm2 logs express-app --err
987
+ ```
988
+
989
+ **Step 2: Implement Error Handlers**
990
+ ```javascript
991
+ // Catch unhandled errors
992
+ process.on('uncaughtException', (error) => {
993
+ console.error('Uncaught exception:', error);
994
+ gracefulShutdown('uncaughtException');
995
+ });
996
+
997
+ process.on('unhandledRejection', (reason, promise) => {
998
+ console.error('Unhandled rejection:', reason);
999
+ gracefulShutdown('unhandledRejection');
1000
+ });
1001
+ ```
1002
+
1003
+ **Step 3: Add Memory Monitoring**
1004
+ ```javascript
1005
+ // ecosystem.config.js
1006
+ module.exports = {
1007
+ apps: [{
1008
+ name: 'express-app',
1009
+ script: './server.js',
1010
+ max_memory_restart: '500M', // Restart on high memory
1011
+ instances: 4,
1012
+ exec_mode: 'cluster',
1013
+ }]
1014
+ };
1015
+ ```
1016
+
1017
+ **Step 4: Check Minimum Uptime**
1018
+ ```javascript
1019
+ // ecosystem.config.js
1020
+ module.exports = {
1021
+ apps: [{
1022
+ min_uptime: '10s', // App must run 10s to be considered healthy
1023
+ max_restarts: 10, // Max restarts within min_uptime period
1024
+ }]
1025
+ };
1026
+ ```
1027
+
1028
+ **Step 5: Test Instance Stability**
1029
+ ```bash
1030
+ # Start single instance first
1031
+ pm2 start server.js --name "test" -i 1
1032
+
1033
+ # If stable, scale up
1034
+ pm2 scale test 4
1035
+ ```
1036
+
1037
+ ### Problem 4: Sessions Not Shared Across Instances
1038
+
1039
+ **Symptoms:**
1040
+ - User logged in but subsequent requests show logged out
1041
+ - Session data inconsistent between requests
1042
+ - Works fine with single instance, breaks with cluster
1043
+
1044
+ **Root Cause:**
1045
+ In-memory session store is not shared across PM2 cluster instances.
1046
+
1047
+ **Solution:**
1048
+
1049
+ **Use Redis for Session Storage:**
1050
+ ```javascript
1051
+ const express = require('express');
1052
+ const session = require('express-session');
1053
+ const RedisStore = require('connect-redis').default;
1054
+ const redis = require('redis');
1055
+
1056
+ const app = express();
1057
+
1058
+ // Redis client
1059
+ const redisClient = redis.createClient({
1060
+ host: process.env.REDIS_HOST || 'localhost',
1061
+ port: process.env.REDIS_PORT || 6379,
1062
+ });
1063
+
1064
+ redisClient.on('error', (err) => console.error('Redis error:', err));
1065
+
1066
+ // Session configuration with Redis store
1067
+ app.use(session({
1068
+ store: new RedisStore({ client: redisClient }),
1069
+ secret: process.env.SESSION_SECRET || 'your-secret-key',
1070
+ resave: false,
1071
+ saveUninitialized: false,
1072
+ cookie: {
1073
+ secure: process.env.NODE_ENV === 'production', // HTTPS only in prod
1074
+ httpOnly: true,
1075
+ maxAge: 1000 * 60 * 60 * 24, // 24 hours
1076
+ },
1077
+ }));
1078
+ ```
1079
+
1080
+ **Alternative: Use JWT (Stateless):**
1081
+ ```javascript
1082
+ const jwt = require('jsonwebtoken');
1083
+
1084
+ // Generate token on login
1085
+ app.post('/login', (req, res) => {
1086
+ // Verify credentials
1087
+ const token = jwt.sign(
1088
+ { userId: user.id },
1089
+ process.env.JWT_SECRET,
1090
+ { expiresIn: '7d' }
1091
+ );
1092
+
1093
+ res.json({ token });
1094
+ });
1095
+
1096
+ // Verify token on protected routes
1097
+ function authMiddleware(req, res, next) {
1098
+ const token = req.headers.authorization?.split(' ')[1];
1099
+
1100
+ if (!token) {
1101
+ return res.status(401).json({ error: 'No token provided' });
1102
+ }
1103
+
1104
+ try {
1105
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
1106
+ req.user = decoded;
1107
+ next();
1108
+ } catch (error) {
1109
+ res.status(401).json({ error: 'Invalid token' });
1110
+ }
1111
+ }
1112
+
1113
+ app.get('/protected', authMiddleware, (req, res) => {
1114
+ res.json({ data: 'secret data', user: req.user });
1115
+ });
1116
+ ```
1117
+
1118
+ ### Problem 5: Nodemon vs PM2 Watch Confusion
1119
+
1120
+ **Symptoms:**
1121
+ - Using PM2 watch mode for development
1122
+ - Slow restarts or unexpected behavior
1123
+ - File changes trigger multiple restarts
1124
+
1125
+ **Root Cause:**
1126
+ PM2 watch mode is not designed for active development and has limitations compared to Nodemon.
1127
+
1128
+ **Solution:**
1129
+
1130
+ **Use Nodemon for Development:**
1131
+ ```json
1132
+ {
1133
+ "scripts": {
1134
+ "dev": "nodemon server.js",
1135
+ "start": "node server.js",
1136
+ "prod": "pm2 start ecosystem.config.js --env production"
1137
+ }
1138
+ }
1139
+ ```
1140
+
1141
+ **Configure Nodemon Properly:**
1142
+ ```json
1143
+ {
1144
+ "watch": ["src"],
1145
+ "ext": "js,json",
1146
+ "ignore": ["src/**/*.test.js"],
1147
+ "exec": "node server.js",
1148
+ "delay": 1000
1149
+ }
1150
+ ```
1151
+
1152
+ **Use PM2 for Production (No Watch):**
1153
+ ```javascript
1154
+ // ecosystem.config.js
1155
+ module.exports = {
1156
+ apps: [{
1157
+ name: 'express-app',
1158
+ script: './server.js',
1159
+ instances: 'max',
1160
+ exec_mode: 'cluster',
1161
+ watch: false, // NEVER true in production
1162
+ autorestart: true,
1163
+ env_production: {
1164
+ NODE_ENV: 'production'
1165
+ }
1166
+ }]
1167
+ };
1168
+ ```
1169
+
1170
+ **Development Workflow:**
1171
+ ```bash
1172
+ # Development: Nodemon
1173
+ npm run dev
1174
+
1175
+ # Production: PM2
1176
+ pm2 start ecosystem.config.js --env production
1177
+ ```
1178
+
1179
+ ## Anti-Patterns
1180
+
1181
+ ### What NOT to Do
1182
+
1183
+ **1. Don't Use PM2 Watch for Development**
1184
+ ```javascript
1185
+ // WRONG
1186
+ module.exports = {
1187
+ apps: [{
1188
+ name: 'express-dev',
1189
+ script: './server.js',
1190
+ watch: true, // Use nodemon instead
1191
+ }]
1192
+ };
1193
+ ```
1194
+
1195
+ Use Nodemon for development, PM2 for production.
1196
+
1197
+ **2. Don't Store Sessions In-Memory with Clustering**
1198
+ ```javascript
1199
+ // WRONG with PM2 cluster mode
1200
+ const session = require('express-session');
1201
+ app.use(session({
1202
+ secret: 'secret',
1203
+ resave: false,
1204
+ saveUninitialized: false,
1205
+ // No store specified = in-memory (breaks clustering)
1206
+ }));
1207
+ ```
1208
+
1209
+ Use Redis or other shared store for sessions.
1210
+
1211
+ **3. Don't Forget Graceful Shutdown**
1212
+ ```javascript
1213
+ // WRONG - abrupt termination
1214
+ app.listen(3000);
1215
+ // No shutdown handlers
1216
+
1217
+ // CORRECT
1218
+ const server = app.listen(3000);
1219
+ process.on('SIGTERM', () => {
1220
+ server.close(() => process.exit(0));
1221
+ });
1222
+ ```
1223
+
1224
+ **4. Don't Block the Event Loop**
1225
+ ```javascript
1226
+ // WRONG - blocks event loop
1227
+ app.get('/compute', (req, res) => {
1228
+ const result = heavyComputation(); // Synchronous!
1229
+ res.json({ result });
1230
+ });
1231
+
1232
+ // CORRECT - offload to worker
1233
+ const { Worker } = require('worker_threads');
1234
+ app.get('/compute', (req, res) => {
1235
+ const worker = new Worker('./worker.js');
1236
+ worker.on('message', (result) => {
1237
+ res.json({ result });
1238
+ });
1239
+ });
1240
+ ```
1241
+
1242
+ **5. Don't Expose Detailed Errors in Production**
1243
+ ```javascript
1244
+ // WRONG
1245
+ app.use((err, req, res, next) => {
1246
+ res.status(500).json({ error: err.stack }); // Leaks info
1247
+ });
1248
+
1249
+ // CORRECT
1250
+ app.use((err, req, res, next) => {
1251
+ if (process.env.NODE_ENV === 'production') {
1252
+ res.status(500).json({ error: 'Internal server error' });
1253
+ } else {
1254
+ res.status(500).json({ error: err.message, stack: err.stack });
1255
+ }
1256
+ });
1257
+ ```
1258
+
1259
+ **6. Don't Use Synchronous File Operations**
1260
+ ```javascript
1261
+ // WRONG
1262
+ const data = fs.readFileSync('./data.json'); // Blocks!
1263
+
1264
+ // CORRECT
1265
+ const data = await fs.promises.readFile('./data.json');
1266
+ ```
1267
+
1268
+ **7. Don't Trust User Input**
1269
+ ```javascript
1270
+ // WRONG - no validation
1271
+ app.post('/users', (req, res) => {
1272
+ const user = new User(req.body); // Dangerous!
1273
+ user.save();
1274
+ });
1275
+
1276
+ // CORRECT
1277
+ const { body, validationResult } = require('express-validator');
1278
+
1279
+ app.post('/users',
1280
+ body('email').isEmail(),
1281
+ body('name').trim().notEmpty(),
1282
+ (req, res) => {
1283
+ const errors = validationResult(req);
1284
+ if (!errors.isEmpty()) {
1285
+ return res.status(400).json({ errors: errors.array() });
1286
+ }
1287
+ // Process validated input
1288
+ }
1289
+ );
1290
+ ```
1291
+
1292
+ ## Quick Reference
1293
+
1294
+ ### Commands
1295
+
1296
+ ```bash
1297
+ # Development with Nodemon
1298
+ npm run dev # Start with nodemon
1299
+ npx nodemon server.js # Direct nodemon
1300
+ npx nodemon --verbose server.js # Verbose output
1301
+ npx nodemon --inspect server.js # Debug mode
1302
+
1303
+ # Production with PM2
1304
+ pm2 start server.js # Start app
1305
+ pm2 start ecosystem.config.js # Start with config
1306
+ pm2 start server.js -i max # Cluster mode (all CPUs)
1307
+ pm2 reload express-app # Zero-downtime reload
1308
+ pm2 restart express-app # Restart
1309
+ pm2 stop express-app # Stop
1310
+ pm2 delete express-app # Remove from PM2
1311
+ pm2 logs express-app # View logs
1312
+ pm2 monit # Monitor dashboard
1313
+ pm2 list # List processes
1314
+ pm2 save # Save process list
1315
+ pm2 startup # Enable auto-start
1316
+
1317
+ # Process Management
1318
+ lsof -ti:3000 | xargs kill -9 # Kill process on port
1319
+ ps aux | grep node # Find Node processes
1320
+ kill -9 <PID> # Kill specific process
1321
+
1322
+ # Scaling
1323
+ pm2 scale express-app 4 # Set to 4 instances
1324
+ pm2 scale express-app +2 # Add 2 instances
1325
+ ```
1326
+
1327
+ ### Configuration Templates
1328
+
1329
+ **package.json:**
1330
+ ```json
1331
+ {
1332
+ "scripts": {
1333
+ "dev": "nodemon server.js",
1334
+ "dev:debug": "nodemon --inspect server.js",
1335
+ "start": "node server.js",
1336
+ "prod": "pm2 start ecosystem.config.js --env production",
1337
+ "reload": "pm2 reload ecosystem.config.js",
1338
+ "stop": "pm2 stop ecosystem.config.js"
1339
+ },
1340
+ "dependencies": {
1341
+ "express": "^4.18.2",
1342
+ "dotenv": "^16.0.3"
1343
+ },
1344
+ "devDependencies": {
1345
+ "nodemon": "^3.0.1"
1346
+ }
1347
+ }
1348
+ ```
1349
+
1350
+ **nodemon.json:**
1351
+ ```json
1352
+ {
1353
+ "watch": ["src"],
1354
+ "ext": "js,json",
1355
+ "ignore": ["src/**/*.test.js"],
1356
+ "exec": "node server.js",
1357
+ "env": {
1358
+ "NODE_ENV": "development"
1359
+ },
1360
+ "delay": 1000
1361
+ }
1362
+ ```
1363
+
1364
+ **ecosystem.config.js (Production):**
1365
+ ```javascript
1366
+ module.exports = {
1367
+ apps: [{
1368
+ name: 'express-app',
1369
+ script: './server.js',
1370
+ instances: 'max',
1371
+ exec_mode: 'cluster',
1372
+ autorestart: true,
1373
+ watch: false,
1374
+ max_memory_restart: '500M',
1375
+ env_production: {
1376
+ NODE_ENV: 'production',
1377
+ PORT: 8080
1378
+ },
1379
+ error_file: './logs/pm2-error.log',
1380
+ out_file: './logs/pm2-out.log',
1381
+ log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
1382
+ merge_logs: true,
1383
+ kill_timeout: 5000,
1384
+ wait_ready: true,
1385
+ listen_timeout: 10000,
1386
+ }]
1387
+ };
1388
+ ```
1389
+
1390
+ **server.js with Graceful Shutdown:**
1391
+ ```javascript
1392
+ const express = require('express');
1393
+ const app = express();
1394
+ const PORT = process.env.PORT || 3000;
1395
+
1396
+ app.get('/health', (req, res) => {
1397
+ res.json({ status: 'ok' });
1398
+ });
1399
+
1400
+ const server = app.listen(PORT, () => {
1401
+ console.log(`Server running on port ${PORT}`);
1402
+ if (process.send) process.send('ready');
1403
+ });
1404
+
1405
+ function gracefulShutdown(signal) {
1406
+ console.log(`${signal} received`);
1407
+ server.close(() => {
1408
+ console.log('Server closed');
1409
+ process.exit(0);
1410
+ });
1411
+ setTimeout(() => process.exit(1), 10000);
1412
+ }
1413
+
1414
+ process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
1415
+ process.on('SIGINT', () => gracefulShutdown('SIGINT'));
1416
+ ```
1417
+
1418
+ ## Related Skills
1419
+
1420
+ - **fastapi-local-dev** - Similar patterns for Python/FastAPI applications
1421
+ - **nextjs-local-dev** - For Next.js with custom Express server
1422
+ - **docker-containerization** - For containerized Express deployments
1423
+ - **systematic-debugging** - For complex debugging scenarios
1424
+
1425
+ ---
1426
+
1427
+ **Express Version Compatibility:** This skill covers Express 4.x and PM2 5.x. Most patterns are stable across versions.
1428
+
1429
+ **Last Updated:** 2024 - Reflects current Express and PM2 best practices for development and production deployment.