clearctx 3.0.0 → 3.1.0

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.
@@ -0,0 +1,853 @@
1
+ ---
2
+ name: nodejs-backend
3
+ description: Production-grade Node.js/Express.js backend patterns for APIs, middleware, error handling, and operational excellence
4
+ domain: backend
5
+ keywords: [nodejs, express, backend, api, middleware, error-handling, logging, validation]
6
+ version: 1.0.0
7
+ ---
8
+
9
+ # Node.js Backend Expertise
10
+
11
+ Production-grade patterns for building robust Express.js APIs with operational excellence.
12
+
13
+ ## Worker Context
14
+
15
+ ### Express.js Project Structure
16
+
17
+ ```
18
+ src/
19
+ ├── controllers/ # HTTP request handling, response formatting
20
+ ├── services/ # Business logic, data orchestration
21
+ ├── repositories/ # Data access layer (DB queries)
22
+ ├── middleware/ # Cross-cutting concerns
23
+ ├── models/ # Data schemas (Zod, Sequelize, Prisma)
24
+ ├── utils/ # Helpers, constants
25
+ └── app.js # Express app setup
26
+ ```
27
+
28
+ **Middleware Execution Order (CRITICAL):**
29
+ ```javascript
30
+ // ALWAYS follow this sequence:
31
+ app.use(cors()); // 1. CORS - first to set headers
32
+ app.use(helmet()); // 2. Security headers
33
+ app.use(rateLimit); // 3. Rate limiting
34
+ app.use(requestIdMiddleware); // 4. Request ID tracking
35
+ app.use(express.json()); // 5. Body parsing
36
+ app.use(authMiddleware); // 6. Authentication
37
+ // Routes with validation middleware
38
+ app.use('/api', routes); // 7. Application routes
39
+ app.use(notFoundHandler); // 8. 404 handler
40
+ app.use(errorHandler); // 9. Error handler (LAST)
41
+ ```
42
+
43
+ ### Centralized Error Handling
44
+
45
+ **AppError Class (CRITICAL - create this first):**
46
+ ```javascript
47
+ // utils/AppError.js
48
+ class AppError extends Error {
49
+ constructor(message, statusCode, code = 'INTERNAL_ERROR') {
50
+ super(message);
51
+ this.statusCode = statusCode;
52
+ this.code = code;
53
+ this.isOperational = true; // vs programmer errors
54
+ Error.captureStackTrace(this, this.constructor);
55
+ }
56
+ }
57
+
58
+ module.exports = AppError;
59
+ ```
60
+
61
+ **Error Handler Middleware:**
62
+ ```javascript
63
+ // middleware/errorHandler.js
64
+ const { logger } = require('../utils/logger');
65
+
66
+ const errorHandler = (err, req, res, next) => {
67
+ // Log error with request context
68
+ logger.error({
69
+ err,
70
+ requestId: req.id,
71
+ method: req.method,
72
+ path: req.path,
73
+ userId: req.user?.id
74
+ });
75
+
76
+ // Operational errors (expected)
77
+ if (err.isOperational) {
78
+ return res.status(err.statusCode).json({
79
+ error: {
80
+ code: err.code,
81
+ message: err.message,
82
+ ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
83
+ }
84
+ });
85
+ }
86
+
87
+ // Programmer errors (unexpected) - don't leak details
88
+ res.status(500).json({
89
+ error: {
90
+ code: 'INTERNAL_ERROR',
91
+ message: 'An unexpected error occurred'
92
+ }
93
+ });
94
+ };
95
+
96
+ module.exports = errorHandler;
97
+ ```
98
+
99
+ **AsyncHandler Wrapper (CRITICAL - use for all async routes):**
100
+ ```javascript
101
+ // utils/asyncHandler.js
102
+ const asyncHandler = (fn) => (req, res, next) => {
103
+ Promise.resolve(fn(req, res, next)).catch(next);
104
+ };
105
+
106
+ module.exports = asyncHandler;
107
+ ```
108
+
109
+ ### Middleware Patterns
110
+
111
+ **Authentication (Bearer Token):**
112
+ ```javascript
113
+ // middleware/auth.js
114
+ const AppError = require('../utils/AppError');
115
+ const { verifyToken } = require('../utils/jwt');
116
+
117
+ const auth = asyncHandler(async (req, res, next) => {
118
+ const authHeader = req.headers.authorization;
119
+
120
+ if (!authHeader?.startsWith('Bearer ')) {
121
+ throw new AppError('Missing or invalid authorization header', 401, 'UNAUTHORIZED');
122
+ }
123
+
124
+ const token = authHeader.substring(7);
125
+ const payload = verifyToken(token); // throws if invalid
126
+
127
+ req.user = payload; // Attach user to request
128
+ next();
129
+ });
130
+ ```
131
+
132
+ **Request Validation (Zod):**
133
+ ```javascript
134
+ // middleware/validate.js
135
+ const { z } = require('zod');
136
+ const AppError = require('../utils/AppError');
137
+
138
+ const validate = (schema) => (req, res, next) => {
139
+ try {
140
+ // Validate body, params, and query
141
+ const validated = schema.parse({
142
+ body: req.body,
143
+ params: req.params,
144
+ query: req.query
145
+ });
146
+
147
+ // Replace with validated/sanitized data
148
+ req.body = validated.body || req.body;
149
+ req.params = validated.params || req.params;
150
+ req.query = validated.query || req.query;
151
+
152
+ next();
153
+ } catch (error) {
154
+ if (error instanceof z.ZodError) {
155
+ throw new AppError(
156
+ 'Validation failed',
157
+ 422,
158
+ 'VALIDATION_ERROR',
159
+ { details: error.errors }
160
+ );
161
+ }
162
+ throw error;
163
+ }
164
+ };
165
+
166
+ // Usage in routes:
167
+ const createUserSchema = z.object({
168
+ body: z.object({
169
+ email: z.string().email(),
170
+ password: z.string().min(8),
171
+ name: z.string().min(1)
172
+ })
173
+ });
174
+
175
+ router.post('/users', validate(createUserSchema), createUser);
176
+ ```
177
+
178
+ **Rate Limiting:**
179
+ ```javascript
180
+ // middleware/rateLimit.js
181
+ const rateLimit = require('express-rate-limit');
182
+ const RedisStore = require('rate-limit-redis');
183
+ const redis = require('../config/redis');
184
+
185
+ const limiter = rateLimit({
186
+ store: new RedisStore({ client: redis }),
187
+ windowMs: 15 * 60 * 1000, // 15 minutes
188
+ max: 100, // limit each IP to 100 requests per windowMs
189
+ message: { error: { code: 'RATE_LIMIT_EXCEEDED', message: 'Too many requests' } },
190
+ standardHeaders: true,
191
+ legacyHeaders: false
192
+ });
193
+ ```
194
+
195
+ **Request ID Tracking:**
196
+ ```javascript
197
+ // middleware/requestId.js
198
+ const { v4: uuidv4 } = require('uuid');
199
+
200
+ const requestId = (req, res, next) => {
201
+ req.id = req.headers['x-request-id'] || uuidv4();
202
+ res.setHeader('X-Request-ID', req.id);
203
+ next();
204
+ };
205
+ ```
206
+
207
+ ### Request Validation Strategy
208
+
209
+ **IMPORTANT: Validate at route level, NOT in controllers**
210
+
211
+ ```javascript
212
+ // schemas/user.schema.js
213
+ const { z } = require('zod');
214
+
215
+ const createUserSchema = z.object({
216
+ body: z.object({
217
+ email: z.string().email().toLowerCase(), // Sanitize
218
+ password: z.string().min(8).max(128),
219
+ name: z.string().min(1).max(100).trim()
220
+ })
221
+ });
222
+
223
+ const updateUserSchema = z.object({
224
+ params: z.object({
225
+ id: z.string().uuid()
226
+ }),
227
+ body: z.object({
228
+ name: z.string().min(1).max(100).trim().optional(),
229
+ bio: z.string().max(500).optional()
230
+ })
231
+ });
232
+
233
+ module.exports = { createUserSchema, updateUserSchema };
234
+ ```
235
+
236
+ ### Response Format Standards
237
+
238
+ **Success Responses:**
239
+ ```javascript
240
+ // GOOD: Consistent success format
241
+ res.status(200).json({ data: users });
242
+ res.status(201).json({ data: newUser });
243
+ res.status(204).send(); // No content
244
+ ```
245
+
246
+ **Error Responses:**
247
+ ```javascript
248
+ // GOOD: Consistent error format
249
+ res.status(400).json({
250
+ error: {
251
+ code: 'INVALID_INPUT',
252
+ message: 'Email is required',
253
+ details: { field: 'email', reason: 'missing' } // Optional
254
+ }
255
+ });
256
+ ```
257
+
258
+ **Status Code Reference Table:**
259
+
260
+ | Code | Use Case | Example |
261
+ |------|----------|---------|
262
+ | 200 | Success (read/update) | `GET /users/:id` |
263
+ | 201 | Created | `POST /users` |
264
+ | 204 | Success, no content | `DELETE /users/:id` |
265
+ | 400 | Bad request | Invalid JSON, malformed data |
266
+ | 401 | Unauthorized | Missing/invalid token |
267
+ | 403 | Forbidden | Valid token, insufficient permissions |
268
+ | 404 | Not found | Resource doesn't exist |
269
+ | 409 | Conflict | Duplicate email, version mismatch |
270
+ | 422 | Validation error | Zod validation failed |
271
+ | 429 | Rate limit exceeded | Too many requests |
272
+ | 500 | Server error | Unexpected error |
273
+
274
+ ### Structured Logging (Pino)
275
+
276
+ **Setup:**
277
+ ```javascript
278
+ // utils/logger.js
279
+ const pino = require('pino');
280
+
281
+ const logger = pino({
282
+ level: process.env.LOG_LEVEL || 'info',
283
+ formatters: {
284
+ level: (label) => ({ level: label })
285
+ },
286
+ redact: {
287
+ paths: ['password', 'token', 'authorization', '*.password', '*.token'],
288
+ remove: true
289
+ }
290
+ });
291
+
292
+ module.exports = { logger };
293
+ ```
294
+
295
+ **Usage:**
296
+ ```javascript
297
+ // GOOD: Structured logging with context
298
+ logger.info({ userId, action: 'login' }, 'User logged in');
299
+ logger.error({ err, requestId: req.id }, 'Database query failed');
300
+
301
+ // BAD: String concatenation, no structure
302
+ logger.info('User ' + userId + ' logged in');
303
+ ```
304
+
305
+ **NEVER Log These (CRITICAL):**
306
+ - Passwords (plain or hashed)
307
+ - API keys, tokens, secrets
308
+ - Credit card numbers, SSN
309
+ - Full request/response bodies (may contain PII)
310
+
311
+ ### Environment Configuration
312
+
313
+ **Fail-Fast Validation:**
314
+ ```javascript
315
+ // config/env.js
316
+ const { z } = require('zod');
317
+ require('dotenv').config();
318
+
319
+ const envSchema = z.object({
320
+ NODE_ENV: z.enum(['development', 'production', 'test']),
321
+ PORT: z.string().transform(Number).pipe(z.number().min(1).max(65535)),
322
+ DATABASE_URL: z.string().url(),
323
+ JWT_SECRET: z.string().min(32),
324
+ REDIS_URL: z.string().url(),
325
+ LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info')
326
+ });
327
+
328
+ // Validate on startup - crash immediately if invalid
329
+ const env = envSchema.parse(process.env);
330
+
331
+ module.exports = env;
332
+ ```
333
+
334
+ **NEVER:**
335
+ - Hardcode secrets in source code
336
+ - Commit .env files to git
337
+ - Use default secrets in production
338
+ - Access `process.env` directly (use validated config)
339
+
340
+ ### Graceful Shutdown
341
+
342
+ **CRITICAL: Drain connections before exit**
343
+ ```javascript
344
+ // server.js
345
+ const gracefulShutdown = async (signal) => {
346
+ logger.info({ signal }, 'Shutdown signal received');
347
+
348
+ // Stop accepting new connections
349
+ server.close(async () => {
350
+ logger.info('HTTP server closed');
351
+
352
+ try {
353
+ // Close database connections
354
+ await db.close();
355
+ logger.info('Database connection closed');
356
+
357
+ // Close Redis connections
358
+ await redis.quit();
359
+ logger.info('Redis connection closed');
360
+
361
+ process.exit(0);
362
+ } catch (err) {
363
+ logger.error({ err }, 'Error during shutdown');
364
+ process.exit(1);
365
+ }
366
+ });
367
+
368
+ // Force shutdown after 30s
369
+ setTimeout(() => {
370
+ logger.error('Forced shutdown after timeout');
371
+ process.exit(1);
372
+ }, 30000);
373
+ };
374
+
375
+ process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
376
+ process.on('SIGINT', () => gracefulShutdown('SIGINT'));
377
+
378
+ // CRITICAL: Handle unhandled rejections
379
+ process.on('unhandledRejection', (reason, promise) => {
380
+ logger.error({ reason, promise }, 'Unhandled Promise Rejection');
381
+ process.exit(1);
382
+ });
383
+ ```
384
+
385
+ ### Performance Patterns
386
+
387
+ **Connection Pooling (PostgreSQL):**
388
+ ```javascript
389
+ // config/database.js
390
+ const { Pool } = require('pg');
391
+
392
+ const pool = new Pool({
393
+ connectionString: env.DATABASE_URL,
394
+ max: 20, // Max connections in pool
395
+ idleTimeoutMillis: 30000,
396
+ connectionTimeoutMillis: 2000
397
+ });
398
+
399
+ // GOOD: Use pool, not new Client() for each query
400
+ const result = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);
401
+ ```
402
+
403
+ **Compression Middleware:**
404
+ ```javascript
405
+ const compression = require('compression');
406
+
407
+ app.use(compression({
408
+ filter: (req, res) => {
409
+ if (req.headers['x-no-compression']) return false;
410
+ return compression.filter(req, res);
411
+ },
412
+ level: 6 // Balance between speed and ratio
413
+ }));
414
+ ```
415
+
416
+ **Streaming Large Payloads:**
417
+ ```javascript
418
+ // GOOD: Stream large responses
419
+ router.get('/export', asyncHandler(async (req, res) => {
420
+ res.setHeader('Content-Type', 'application/json');
421
+ res.setHeader('Content-Disposition', 'attachment; filename="export.json"');
422
+
423
+ const stream = await dataService.streamExport();
424
+ stream.pipe(res);
425
+ }));
426
+
427
+ // BAD: Load everything into memory
428
+ const allData = await dataService.getAll(); // OOM risk
429
+ res.json(allData);
430
+ ```
431
+
432
+ ### Controller-Service-Repository Pattern
433
+
434
+ **Controller (THIN - only HTTP concerns):**
435
+ ```javascript
436
+ // controllers/user.controller.js
437
+ const userService = require('../services/user.service');
438
+ const asyncHandler = require('../utils/asyncHandler');
439
+
440
+ exports.createUser = asyncHandler(async (req, res) => {
441
+ const user = await userService.createUser(req.body);
442
+ res.status(201).json({ data: user });
443
+ });
444
+
445
+ exports.getUser = asyncHandler(async (req, res) => {
446
+ const user = await userService.getUser(req.params.id);
447
+ res.json({ data: user });
448
+ });
449
+ ```
450
+
451
+ **Service (Business Logic):**
452
+ ```javascript
453
+ // services/user.service.js
454
+ const userRepository = require('../repositories/user.repository');
455
+ const AppError = require('../utils/AppError');
456
+ const { hashPassword } = require('../utils/password');
457
+
458
+ exports.createUser = async (data) => {
459
+ const exists = await userRepository.findByEmail(data.email);
460
+ if (exists) {
461
+ throw new AppError('Email already in use', 409, 'EMAIL_CONFLICT');
462
+ }
463
+
464
+ const hashedPassword = await hashPassword(data.password);
465
+ return userRepository.create({ ...data, password: hashedPassword });
466
+ };
467
+
468
+ exports.getUser = async (id) => {
469
+ const user = await userRepository.findById(id);
470
+ if (!user) {
471
+ throw new AppError('User not found', 404, 'USER_NOT_FOUND');
472
+ }
473
+ return user;
474
+ };
475
+ ```
476
+
477
+ **Repository (Data Access):**
478
+ ```javascript
479
+ // repositories/user.repository.js
480
+ const pool = require('../config/database');
481
+
482
+ exports.create = async (data) => {
483
+ const result = await pool.query(
484
+ 'INSERT INTO users (email, password, name) VALUES ($1, $2, $3) RETURNING *',
485
+ [data.email, data.password, data.name]
486
+ );
487
+ return result.rows[0];
488
+ };
489
+
490
+ exports.findById = async (id) => {
491
+ const result = await pool.query('SELECT * FROM users WHERE id = $1', [id]);
492
+ return result.rows[0];
493
+ };
494
+
495
+ exports.findByEmail = async (email) => {
496
+ const result = await pool.query('SELECT * FROM users WHERE email = $1', [email]);
497
+ return result.rows[0];
498
+ };
499
+ ```
500
+
501
+ ## Conventions
502
+
503
+ ### File Naming
504
+ - **Files:** `camelCase.js` (userController.js, authMiddleware.js)
505
+ - **Classes:** `PascalCase` (AppError, UserService)
506
+ - **Constants:** `UPPER_SNAKE_CASE` (MAX_LOGIN_ATTEMPTS)
507
+
508
+ ### Database
509
+ - **Columns:** `snake_case` (user_id, created_at)
510
+ - **Tables:** `plural` (users, orders, audit_logs)
511
+ - **Foreign keys:** `{table}_id` (user_id, order_id)
512
+
513
+ ### Variable Naming
514
+ - **JavaScript:** `camelCase` (userId, requestData)
515
+ - **SQL params:** `$1, $2` (PostgreSQL) or `?` (MySQL)
516
+
517
+ ### Boolean Handling
518
+ - **Database:** `BOOLEAN` type (true/false)
519
+ - **JavaScript:** `true/false` (not 1/0)
520
+ - **Never:** String booleans ("true", "false")
521
+
522
+ ### Date Handling
523
+ - **Storage:** UTC timestamps (`TIMESTAMP WITH TIME ZONE`)
524
+ - **API:** ISO 8601 strings (`2026-02-15T10:30:00Z`)
525
+ - **NEVER:** Unix timestamps in APIs (use ISO strings)
526
+
527
+ ### Audit Logging
528
+ - **Action names:** lowercase verbs (`created`, `updated`, `deleted`)
529
+ - **Include:** userId, action, resourceType, resourceId, timestamp, changes (optional)
530
+
531
+ ## Common Patterns
532
+
533
+ ### 1. Paginated API Response
534
+
535
+ ```javascript
536
+ // GOOD: Consistent pagination
537
+ router.get('/users', asyncHandler(async (req, res) => {
538
+ const page = parseInt(req.query.page) || 1;
539
+ const limit = parseInt(req.query.limit) || 20;
540
+ const offset = (page - 1) * limit;
541
+
542
+ const { rows: users, count } = await userRepository.findAll({ limit, offset });
543
+
544
+ res.json({
545
+ data: users,
546
+ pagination: {
547
+ page,
548
+ limit,
549
+ total: count,
550
+ totalPages: Math.ceil(count / limit)
551
+ }
552
+ });
553
+ }));
554
+ ```
555
+
556
+ ### 2. Transaction Wrapper
557
+
558
+ ```javascript
559
+ // utils/transaction.js
560
+ const pool = require('../config/database');
561
+
562
+ const withTransaction = async (callback) => {
563
+ const client = await pool.connect();
564
+ try {
565
+ await client.query('BEGIN');
566
+ const result = await callback(client);
567
+ await client.query('COMMIT');
568
+ return result;
569
+ } catch (error) {
570
+ await client.query('ROLLBACK');
571
+ throw error;
572
+ } finally {
573
+ client.release();
574
+ }
575
+ };
576
+
577
+ // Usage in service:
578
+ exports.transferFunds = async (fromId, toId, amount) => {
579
+ return withTransaction(async (client) => {
580
+ await client.query('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [amount, fromId]);
581
+ await client.query('UPDATE accounts SET balance = balance + $1 WHERE id = $2', [amount, toId]);
582
+ return { success: true };
583
+ });
584
+ };
585
+ ```
586
+
587
+ ### 3. Dependency Injection for Testing
588
+
589
+ ```javascript
590
+ // GOOD: Inject dependencies
591
+ class UserService {
592
+ constructor(userRepository, emailService) {
593
+ this.userRepository = userRepository;
594
+ this.emailService = emailService;
595
+ }
596
+
597
+ async createUser(data) {
598
+ const user = await this.userRepository.create(data);
599
+ await this.emailService.sendWelcome(user.email);
600
+ return user;
601
+ }
602
+ }
603
+
604
+ // Easy to mock in tests:
605
+ const mockRepo = { create: jest.fn() };
606
+ const mockEmail = { sendWelcome: jest.fn() };
607
+ const service = new UserService(mockRepo, mockEmail);
608
+ ```
609
+
610
+ ### 4. Request Context Logger
611
+
612
+ ```javascript
613
+ // middleware/requestLogger.js
614
+ const { logger } = require('../utils/logger');
615
+
616
+ const requestLogger = (req, res, next) => {
617
+ const start = Date.now();
618
+
619
+ // Create child logger with request context
620
+ req.log = logger.child({ requestId: req.id });
621
+
622
+ res.on('finish', () => {
623
+ req.log.info({
624
+ method: req.method,
625
+ path: req.path,
626
+ statusCode: res.statusCode,
627
+ duration: Date.now() - start,
628
+ userId: req.user?.id
629
+ }, 'Request completed');
630
+ });
631
+
632
+ next();
633
+ };
634
+
635
+ // Usage in controllers:
636
+ req.log.info({ orderId }, 'Processing order');
637
+ ```
638
+
639
+ ### 5. Feature Flags
640
+
641
+ ```javascript
642
+ // utils/features.js
643
+ const features = {
644
+ newCheckout: process.env.FEATURE_NEW_CHECKOUT === 'true',
645
+ betaSearch: process.env.FEATURE_BETA_SEARCH === 'true'
646
+ };
647
+
648
+ const isEnabled = (feature) => features[feature] || false;
649
+
650
+ // Usage:
651
+ if (isEnabled('newCheckout')) {
652
+ return newCheckoutService.process(order);
653
+ }
654
+ return legacyCheckoutService.process(order);
655
+ ```
656
+
657
+ ## Anti-Patterns
658
+
659
+ ### 1. Callback Hell
660
+
661
+ **BAD:**
662
+ ```javascript
663
+ getUserById(id, (err, user) => {
664
+ if (err) return handleError(err);
665
+ getOrders(user.id, (err, orders) => {
666
+ if (err) return handleError(err);
667
+ processOrders(orders, (err, result) => {
668
+ if (err) return handleError(err);
669
+ sendEmail(result, (err) => {
670
+ if (err) return handleError(err);
671
+ });
672
+ });
673
+ });
674
+ });
675
+ ```
676
+
677
+ **GOOD:**
678
+ ```javascript
679
+ const user = await userService.getById(id);
680
+ const orders = await orderService.getByUserId(user.id);
681
+ const result = await orderService.process(orders);
682
+ await emailService.send(result);
683
+ ```
684
+
685
+ ### 2. Fat Controllers
686
+
687
+ **BAD:**
688
+ ```javascript
689
+ // Controller doing business logic AND data access
690
+ exports.createUser = asyncHandler(async (req, res) => {
691
+ const exists = await pool.query('SELECT * FROM users WHERE email = $1', [req.body.email]);
692
+ if (exists.rows.length > 0) {
693
+ throw new AppError('Email exists', 409);
694
+ }
695
+
696
+ const hashed = await bcrypt.hash(req.body.password, 10);
697
+ const result = await pool.query(
698
+ 'INSERT INTO users (email, password) VALUES ($1, $2) RETURNING *',
699
+ [req.body.email, hashed]
700
+ );
701
+
702
+ await sendEmail(result.rows[0].email, 'Welcome!');
703
+ res.status(201).json({ data: result.rows[0] });
704
+ });
705
+ ```
706
+
707
+ **GOOD:**
708
+ ```javascript
709
+ // Controller delegates to service
710
+ exports.createUser = asyncHandler(async (req, res) => {
711
+ const user = await userService.createUser(req.body);
712
+ res.status(201).json({ data: user });
713
+ });
714
+ ```
715
+
716
+ ### 3. Ignoring Errors
717
+
718
+ **BAD:**
719
+ ```javascript
720
+ // Swallowing errors
721
+ try {
722
+ await riskyOperation();
723
+ } catch (err) {
724
+ // Silent failure - user never knows something broke
725
+ }
726
+
727
+ // Or worse: empty catch
728
+ doSomething().catch(() => {});
729
+ ```
730
+
731
+ **GOOD:**
732
+ ```javascript
733
+ // Proper error handling
734
+ try {
735
+ await riskyOperation();
736
+ } catch (err) {
737
+ logger.error({ err }, 'Risky operation failed');
738
+ throw new AppError('Operation failed', 500, 'OPERATION_ERROR');
739
+ }
740
+ ```
741
+
742
+ ### 4. Blocking the Event Loop
743
+
744
+ **BAD:**
745
+ ```javascript
746
+ // Synchronous crypto operations block the event loop
747
+ const bcrypt = require('bcrypt');
748
+ const hashed = bcrypt.hashSync(password, 10); // BLOCKS for ~100ms
749
+
750
+ // Heavy computation in request handler
751
+ router.get('/report', (req, res) => {
752
+ const data = processMillionsOfRecords(); // Blocks all other requests
753
+ res.json(data);
754
+ });
755
+ ```
756
+
757
+ **GOOD:**
758
+ ```javascript
759
+ // Use async versions
760
+ const hashed = await bcrypt.hash(password, 10);
761
+
762
+ // Offload heavy work to worker threads or queues
763
+ const { Worker } = require('worker_threads');
764
+ router.get('/report', asyncHandler(async (req, res) => {
765
+ const jobId = await jobQueue.add('generateReport', req.query);
766
+ res.status(202).json({ data: { jobId, status: 'processing' } });
767
+ }));
768
+ ```
769
+
770
+ ### 5. SQL Injection
771
+
772
+ **BAD:**
773
+ ```javascript
774
+ // NEVER concatenate user input into SQL
775
+ const userId = req.params.id;
776
+ const query = `SELECT * FROM users WHERE id = '${userId}'`; // VULNERABLE
777
+ const result = await pool.query(query);
778
+ ```
779
+
780
+ **GOOD:**
781
+ ```javascript
782
+ // ALWAYS use parameterized queries
783
+ const userId = req.params.id;
784
+ const result = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);
785
+ ```
786
+
787
+ ## Integration Notes
788
+
789
+ ### Team Coordination
790
+
791
+ When working as part of a multi-session team:
792
+
793
+ 1. **Startup:** Call `team_check_inbox()` FIRST to get messages/artifacts from other workers
794
+ 2. **Consume Conventions:** Read `shared-conventions` artifact to align on response format, status codes, naming
795
+ 3. **Consume Database Schema:** Read schema artifact from database worker to match table/column names exactly
796
+ 4. **Publish API Contract:** After implementing routes, publish artifact with:
797
+ ```json
798
+ {
799
+ "routes": [
800
+ { "method": "POST", "path": "/api/users", "auth": true, "body": "CreateUserSchema" },
801
+ { "method": "GET", "path": "/api/users/:id", "auth": true, "response": "UserSchema" }
802
+ ],
803
+ "schemas": { "CreateUserSchema": {...}, "UserSchema": {...} },
804
+ "errorCodes": ["VALIDATION_ERROR", "USER_NOT_FOUND", "EMAIL_CONFLICT"]
805
+ }
806
+ ```
807
+ 5. **Coordinate with Frontend:** Frontend worker consumes your API contract artifact
808
+ 6. **Broadcast Completion:** Use `team_broadcast()` when routes are ready for integration testing
809
+ 7. **File Paths:** ALWAYS use relative paths (e.g., `src/controllers/user.js`), NEVER absolute paths
810
+
811
+ ### Database Worker Coordination
812
+
813
+ **CRITICAL: Match database worker's schema exactly**
814
+ - Read their artifact for table names, column names, data types
815
+ - Use their naming conventions (e.g., `user_id` not `userId` in SQL)
816
+ - Follow their foreign key patterns
817
+ - Never create tables yourself - coordinate schema changes
818
+
819
+ ### Frontend Worker Coordination
820
+
821
+ **Provide clear API contract:**
822
+ - Document all endpoints (method, path, auth requirement)
823
+ - Provide request/response schemas (use Zod schemas as source of truth)
824
+ - List all possible error codes
825
+ - Document pagination format
826
+ - Document authentication (Bearer token in `Authorization` header)
827
+
828
+ **Example artifact:**
829
+ ```json
830
+ {
831
+ "baseUrl": "/api/v1",
832
+ "authentication": "Bearer token in Authorization header",
833
+ "responseFormat": {
834
+ "success": { "data": "<resource>" },
835
+ "error": { "error": { "code": "string", "message": "string" } }
836
+ },
837
+ "endpoints": [...]
838
+ }
839
+ ```
840
+
841
+ ### Testing Coordination
842
+
843
+ **For integration tests:**
844
+ - Seed database with test data (coordinate with database worker)
845
+ - Provide test fixtures (sample requests/responses)
846
+ - Document test user credentials
847
+ - Clear test data between runs
848
+
849
+ ---
850
+
851
+ **Version:** 1.0.0
852
+ **Last Updated:** 2026-02-15
853
+ **Token Count:** ~1950 (under 2000 limit for Worker Context)