@arcadialdev/arcality 2.2.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.
Files changed (97) hide show
  1. package/.agents/skills/e2e-testing-expert/SKILL.md +28 -0
  2. package/.agents/skills/frontend-design/LICENSE.txt +177 -0
  3. package/.agents/skills/frontend-design/SKILL.md +42 -0
  4. package/.agents/skills/nodejs-backend-patterns/SKILL.md +639 -0
  5. package/.agents/skills/nodejs-backend-patterns/references/advanced-patterns.md +430 -0
  6. package/.agents/skills/playwright-best-practices/LICENSE.md +7 -0
  7. package/.agents/skills/playwright-best-practices/README.md +147 -0
  8. package/.agents/skills/playwright-best-practices/SKILL.md +303 -0
  9. package/.agents/skills/playwright-best-practices/advanced/authentication-flows.md +360 -0
  10. package/.agents/skills/playwright-best-practices/advanced/authentication.md +871 -0
  11. package/.agents/skills/playwright-best-practices/advanced/clock-mocking.md +364 -0
  12. package/.agents/skills/playwright-best-practices/advanced/mobile-testing.md +409 -0
  13. package/.agents/skills/playwright-best-practices/advanced/multi-context.md +288 -0
  14. package/.agents/skills/playwright-best-practices/advanced/multi-user.md +393 -0
  15. package/.agents/skills/playwright-best-practices/advanced/network-advanced.md +452 -0
  16. package/.agents/skills/playwright-best-practices/advanced/third-party.md +464 -0
  17. package/.agents/skills/playwright-best-practices/architecture/pom-vs-fixtures.md +363 -0
  18. package/.agents/skills/playwright-best-practices/architecture/test-architecture.md +369 -0
  19. package/.agents/skills/playwright-best-practices/architecture/when-to-mock.md +383 -0
  20. package/.agents/skills/playwright-best-practices/browser-apis/browser-apis.md +391 -0
  21. package/.agents/skills/playwright-best-practices/browser-apis/iframes.md +403 -0
  22. package/.agents/skills/playwright-best-practices/browser-apis/service-workers.md +504 -0
  23. package/.agents/skills/playwright-best-practices/browser-apis/websockets.md +403 -0
  24. package/.agents/skills/playwright-best-practices/core/annotations.md +424 -0
  25. package/.agents/skills/playwright-best-practices/core/assertions-waiting.md +361 -0
  26. package/.agents/skills/playwright-best-practices/core/configuration.md +452 -0
  27. package/.agents/skills/playwright-best-practices/core/fixtures-hooks.md +417 -0
  28. package/.agents/skills/playwright-best-practices/core/global-setup.md +434 -0
  29. package/.agents/skills/playwright-best-practices/core/locators.md +242 -0
  30. package/.agents/skills/playwright-best-practices/core/page-object-model.md +315 -0
  31. package/.agents/skills/playwright-best-practices/core/projects-dependencies.md +453 -0
  32. package/.agents/skills/playwright-best-practices/core/test-data.md +492 -0
  33. package/.agents/skills/playwright-best-practices/core/test-suite-structure.md +361 -0
  34. package/.agents/skills/playwright-best-practices/core/test-tags.md +298 -0
  35. package/.agents/skills/playwright-best-practices/debugging/console-errors.md +420 -0
  36. package/.agents/skills/playwright-best-practices/debugging/debugging.md +504 -0
  37. package/.agents/skills/playwright-best-practices/debugging/error-testing.md +360 -0
  38. package/.agents/skills/playwright-best-practices/debugging/flaky-tests.md +496 -0
  39. package/.agents/skills/playwright-best-practices/frameworks/angular.md +530 -0
  40. package/.agents/skills/playwright-best-practices/frameworks/nextjs.md +469 -0
  41. package/.agents/skills/playwright-best-practices/frameworks/react.md +531 -0
  42. package/.agents/skills/playwright-best-practices/frameworks/vue.md +574 -0
  43. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/ci-cd.md +468 -0
  44. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/docker.md +283 -0
  45. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/github-actions.md +546 -0
  46. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/gitlab.md +397 -0
  47. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/other-providers.md +521 -0
  48. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/parallel-sharding.md +371 -0
  49. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/performance.md +453 -0
  50. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/reporting.md +424 -0
  51. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/test-coverage.md +497 -0
  52. package/.agents/skills/playwright-best-practices/testing-patterns/accessibility.md +359 -0
  53. package/.agents/skills/playwright-best-practices/testing-patterns/api-testing.md +719 -0
  54. package/.agents/skills/playwright-best-practices/testing-patterns/browser-extensions.md +506 -0
  55. package/.agents/skills/playwright-best-practices/testing-patterns/canvas-webgl.md +493 -0
  56. package/.agents/skills/playwright-best-practices/testing-patterns/component-testing.md +500 -0
  57. package/.agents/skills/playwright-best-practices/testing-patterns/drag-drop.md +576 -0
  58. package/.agents/skills/playwright-best-practices/testing-patterns/electron.md +509 -0
  59. package/.agents/skills/playwright-best-practices/testing-patterns/file-operations.md +377 -0
  60. package/.agents/skills/playwright-best-practices/testing-patterns/file-upload-download.md +562 -0
  61. package/.agents/skills/playwright-best-practices/testing-patterns/forms-validation.md +561 -0
  62. package/.agents/skills/playwright-best-practices/testing-patterns/graphql-testing.md +331 -0
  63. package/.agents/skills/playwright-best-practices/testing-patterns/i18n.md +508 -0
  64. package/.agents/skills/playwright-best-practices/testing-patterns/performance-testing.md +476 -0
  65. package/.agents/skills/playwright-best-practices/testing-patterns/security-testing.md +430 -0
  66. package/.agents/skills/playwright-best-practices/testing-patterns/visual-regression.md +634 -0
  67. package/.env.example +21 -0
  68. package/README.md +30 -0
  69. package/bin/arcality.mjs +86 -0
  70. package/package.json +66 -0
  71. package/playwright.config.ts +12 -0
  72. package/scripts/cleanup-qmsdev.mjs +63 -0
  73. package/scripts/discover-view.mjs +52 -0
  74. package/scripts/extract-view.mjs +64 -0
  75. package/scripts/gen-and-run.mjs +838 -0
  76. package/scripts/init.mjs +290 -0
  77. package/scripts/migrate-to-central-out.mjs +157 -0
  78. package/scripts/postinstall.mjs +63 -0
  79. package/scripts/rebrand-report.mjs +241 -0
  80. package/scripts/setup.mjs +166 -0
  81. package/src/KnowledgeService.ts +239 -0
  82. package/src/arcalityClient.mjs +266 -0
  83. package/src/configLoader.mjs +179 -0
  84. package/src/configManager.mjs +172 -0
  85. package/src/consoleBanner.ts +32 -0
  86. package/src/envSetup.ts +205 -0
  87. package/src/index.ts +25 -0
  88. package/src/projectInspector.ts +42 -0
  89. package/src/services/collectiveMemoryService.ts +178 -0
  90. package/src/testRunner.ts +201 -0
  91. package/tests/_helpers/ArcalityReporter.ts +490 -0
  92. package/tests/_helpers/agentic-runner.spec.ts +741 -0
  93. package/tests/_helpers/ai-agent-helper.ts +1573 -0
  94. package/tests/_helpers/discover-view.spec.ts +238 -0
  95. package/tests/_helpers/extract-view.spec.ts +118 -0
  96. package/tests/_helpers/qa-tools.ts +333 -0
  97. package/tests/_helpers/smart-action.spec.ts +1458 -0
@@ -0,0 +1,639 @@
1
+ ---
2
+ name: nodejs-backend-patterns
3
+ description: Build production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures.
4
+ ---
5
+
6
+ # Node.js Backend Patterns
7
+
8
+ Comprehensive guidance for building scalable, maintainable, and production-ready Node.js backend applications with modern frameworks, architectural patterns, and best practices.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - Building REST APIs or GraphQL servers
13
+ - Creating microservices with Node.js
14
+ - Implementing authentication and authorization
15
+ - Designing scalable backend architectures
16
+ - Setting up middleware and error handling
17
+ - Integrating databases (SQL and NoSQL)
18
+ - Building real-time applications with WebSockets
19
+ - Implementing background job processing
20
+
21
+ ## Core Frameworks
22
+
23
+ ### Express.js - Minimalist Framework
24
+
25
+ **Basic Setup:**
26
+
27
+ ```typescript
28
+ import express, { Request, Response, NextFunction } from "express";
29
+ import helmet from "helmet";
30
+ import cors from "cors";
31
+ import compression from "compression";
32
+
33
+ const app = express();
34
+
35
+ // Security middleware
36
+ app.use(helmet());
37
+ app.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(",") }));
38
+ app.use(compression());
39
+
40
+ // Body parsing
41
+ app.use(express.json({ limit: "10mb" }));
42
+ app.use(express.urlencoded({ extended: true, limit: "10mb" }));
43
+
44
+ // Request logging
45
+ app.use((req: Request, res: Response, next: NextFunction) => {
46
+ console.log(`${req.method} ${req.path}`);
47
+ next();
48
+ });
49
+
50
+ const PORT = process.env.PORT || 3000;
51
+ app.listen(PORT, () => {
52
+ console.log(`Server running on port ${PORT}`);
53
+ });
54
+ ```
55
+
56
+ ### Fastify - High Performance Framework
57
+
58
+ **Basic Setup:**
59
+
60
+ ```typescript
61
+ import Fastify from "fastify";
62
+ import helmet from "@fastify/helmet";
63
+ import cors from "@fastify/cors";
64
+ import compress from "@fastify/compress";
65
+
66
+ const fastify = Fastify({
67
+ logger: {
68
+ level: process.env.LOG_LEVEL || "info",
69
+ transport: {
70
+ target: "pino-pretty",
71
+ options: { colorize: true },
72
+ },
73
+ },
74
+ });
75
+
76
+ // Plugins
77
+ await fastify.register(helmet);
78
+ await fastify.register(cors, { origin: true });
79
+ await fastify.register(compress);
80
+
81
+ // Type-safe routes with schema validation
82
+ fastify.post<{
83
+ Body: { name: string; email: string };
84
+ Reply: { id: string; name: string };
85
+ }>(
86
+ "/users",
87
+ {
88
+ schema: {
89
+ body: {
90
+ type: "object",
91
+ required: ["name", "email"],
92
+ properties: {
93
+ name: { type: "string", minLength: 1 },
94
+ email: { type: "string", format: "email" },
95
+ },
96
+ },
97
+ },
98
+ },
99
+ async (request, reply) => {
100
+ const { name, email } = request.body;
101
+ return { id: "123", name };
102
+ },
103
+ );
104
+
105
+ await fastify.listen({ port: 3000, host: "0.0.0.0" });
106
+ ```
107
+
108
+ ## Architectural Patterns
109
+
110
+ ### Pattern 1: Layered Architecture
111
+
112
+ **Structure:**
113
+
114
+ ```
115
+ src/
116
+ ├── controllers/ # Handle HTTP requests/responses
117
+ ├── services/ # Business logic
118
+ ├── repositories/ # Data access layer
119
+ ├── models/ # Data models
120
+ ├── middleware/ # Express/Fastify middleware
121
+ ├── routes/ # Route definitions
122
+ ├── utils/ # Helper functions
123
+ ├── config/ # Configuration
124
+ └── types/ # TypeScript types
125
+ ```
126
+
127
+ **Controller Layer:**
128
+
129
+ ```typescript
130
+ // controllers/user.controller.ts
131
+ import { Request, Response, NextFunction } from "express";
132
+ import { UserService } from "../services/user.service";
133
+ import { CreateUserDTO, UpdateUserDTO } from "../types/user.types";
134
+
135
+ export class UserController {
136
+ constructor(private userService: UserService) {}
137
+
138
+ async createUser(req: Request, res: Response, next: NextFunction) {
139
+ try {
140
+ const userData: CreateUserDTO = req.body;
141
+ const user = await this.userService.createUser(userData);
142
+ res.status(201).json(user);
143
+ } catch (error) {
144
+ next(error);
145
+ }
146
+ }
147
+
148
+ async getUser(req: Request, res: Response, next: NextFunction) {
149
+ try {
150
+ const { id } = req.params;
151
+ const user = await this.userService.getUserById(id);
152
+ res.json(user);
153
+ } catch (error) {
154
+ next(error);
155
+ }
156
+ }
157
+
158
+ async updateUser(req: Request, res: Response, next: NextFunction) {
159
+ try {
160
+ const { id } = req.params;
161
+ const updates: UpdateUserDTO = req.body;
162
+ const user = await this.userService.updateUser(id, updates);
163
+ res.json(user);
164
+ } catch (error) {
165
+ next(error);
166
+ }
167
+ }
168
+
169
+ async deleteUser(req: Request, res: Response, next: NextFunction) {
170
+ try {
171
+ const { id } = req.params;
172
+ await this.userService.deleteUser(id);
173
+ res.status(204).send();
174
+ } catch (error) {
175
+ next(error);
176
+ }
177
+ }
178
+ }
179
+ ```
180
+
181
+ **Service Layer:**
182
+
183
+ ```typescript
184
+ // services/user.service.ts
185
+ import { UserRepository } from "../repositories/user.repository";
186
+ import { CreateUserDTO, UpdateUserDTO, User } from "../types/user.types";
187
+ import { NotFoundError, ValidationError } from "../utils/errors";
188
+ import bcrypt from "bcrypt";
189
+
190
+ export class UserService {
191
+ constructor(private userRepository: UserRepository) {}
192
+
193
+ async createUser(userData: CreateUserDTO): Promise<User> {
194
+ // Validation
195
+ const existingUser = await this.userRepository.findByEmail(userData.email);
196
+ if (existingUser) {
197
+ throw new ValidationError("Email already exists");
198
+ }
199
+
200
+ // Hash password
201
+ const hashedPassword = await bcrypt.hash(userData.password, 10);
202
+
203
+ // Create user
204
+ const user = await this.userRepository.create({
205
+ ...userData,
206
+ password: hashedPassword,
207
+ });
208
+
209
+ // Remove password from response
210
+ const { password, ...userWithoutPassword } = user;
211
+ return userWithoutPassword as User;
212
+ }
213
+
214
+ async getUserById(id: string): Promise<User> {
215
+ const user = await this.userRepository.findById(id);
216
+ if (!user) {
217
+ throw new NotFoundError("User not found");
218
+ }
219
+ const { password, ...userWithoutPassword } = user;
220
+ return userWithoutPassword as User;
221
+ }
222
+
223
+ async updateUser(id: string, updates: UpdateUserDTO): Promise<User> {
224
+ const user = await this.userRepository.update(id, updates);
225
+ if (!user) {
226
+ throw new NotFoundError("User not found");
227
+ }
228
+ const { password, ...userWithoutPassword } = user;
229
+ return userWithoutPassword as User;
230
+ }
231
+
232
+ async deleteUser(id: string): Promise<void> {
233
+ const deleted = await this.userRepository.delete(id);
234
+ if (!deleted) {
235
+ throw new NotFoundError("User not found");
236
+ }
237
+ }
238
+ }
239
+ ```
240
+
241
+ **Repository Layer:**
242
+
243
+ ```typescript
244
+ // repositories/user.repository.ts
245
+ import { Pool } from "pg";
246
+ import { CreateUserDTO, UpdateUserDTO, UserEntity } from "../types/user.types";
247
+
248
+ export class UserRepository {
249
+ constructor(private db: Pool) {}
250
+
251
+ async create(
252
+ userData: CreateUserDTO & { password: string },
253
+ ): Promise<UserEntity> {
254
+ const query = `
255
+ INSERT INTO users (name, email, password)
256
+ VALUES ($1, $2, $3)
257
+ RETURNING id, name, email, password, created_at, updated_at
258
+ `;
259
+ const { rows } = await this.db.query(query, [
260
+ userData.name,
261
+ userData.email,
262
+ userData.password,
263
+ ]);
264
+ return rows[0];
265
+ }
266
+
267
+ async findById(id: string): Promise<UserEntity | null> {
268
+ const query = "SELECT * FROM users WHERE id = $1";
269
+ const { rows } = await this.db.query(query, [id]);
270
+ return rows[0] || null;
271
+ }
272
+
273
+ async findByEmail(email: string): Promise<UserEntity | null> {
274
+ const query = "SELECT * FROM users WHERE email = $1";
275
+ const { rows } = await this.db.query(query, [email]);
276
+ return rows[0] || null;
277
+ }
278
+
279
+ async update(id: string, updates: UpdateUserDTO): Promise<UserEntity | null> {
280
+ const fields = Object.keys(updates);
281
+ const values = Object.values(updates);
282
+
283
+ const setClause = fields
284
+ .map((field, idx) => `${field} = $${idx + 2}`)
285
+ .join(", ");
286
+
287
+ const query = `
288
+ UPDATE users
289
+ SET ${setClause}, updated_at = CURRENT_TIMESTAMP
290
+ WHERE id = $1
291
+ RETURNING *
292
+ `;
293
+
294
+ const { rows } = await this.db.query(query, [id, ...values]);
295
+ return rows[0] || null;
296
+ }
297
+
298
+ async delete(id: string): Promise<boolean> {
299
+ const query = "DELETE FROM users WHERE id = $1";
300
+ const { rowCount } = await this.db.query(query, [id]);
301
+ return rowCount > 0;
302
+ }
303
+ }
304
+ ```
305
+
306
+ ### Pattern 2: Dependency Injection
307
+
308
+ Use a DI container to wire up repositories, services, and controllers. For a full container implementation, see [references/advanced-patterns.md](references/advanced-patterns.md).
309
+
310
+ ## Middleware Patterns
311
+
312
+ ### Authentication Middleware
313
+
314
+ ```typescript
315
+ // middleware/auth.middleware.ts
316
+ import { Request, Response, NextFunction } from "express";
317
+ import jwt from "jsonwebtoken";
318
+ import { UnauthorizedError } from "../utils/errors";
319
+
320
+ interface JWTPayload {
321
+ userId: string;
322
+ email: string;
323
+ }
324
+
325
+ declare global {
326
+ namespace Express {
327
+ interface Request {
328
+ user?: JWTPayload;
329
+ }
330
+ }
331
+ }
332
+
333
+ export const authenticate = async (
334
+ req: Request,
335
+ res: Response,
336
+ next: NextFunction,
337
+ ) => {
338
+ try {
339
+ const token = req.headers.authorization?.replace("Bearer ", "");
340
+
341
+ if (!token) {
342
+ throw new UnauthorizedError("No token provided");
343
+ }
344
+
345
+ const payload = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload;
346
+
347
+ req.user = payload;
348
+ next();
349
+ } catch (error) {
350
+ next(new UnauthorizedError("Invalid token"));
351
+ }
352
+ };
353
+
354
+ export const authorize = (...roles: string[]) => {
355
+ return async (req: Request, res: Response, next: NextFunction) => {
356
+ if (!req.user) {
357
+ return next(new UnauthorizedError("Not authenticated"));
358
+ }
359
+
360
+ // Check if user has required role
361
+ const hasRole = roles.some((role) => req.user?.roles?.includes(role));
362
+
363
+ if (!hasRole) {
364
+ return next(new UnauthorizedError("Insufficient permissions"));
365
+ }
366
+
367
+ next();
368
+ };
369
+ };
370
+ ```
371
+
372
+ ### Validation Middleware
373
+
374
+ ```typescript
375
+ // middleware/validation.middleware.ts
376
+ import { Request, Response, NextFunction } from "express";
377
+ import { AnyZodObject, ZodError } from "zod";
378
+ import { ValidationError } from "../utils/errors";
379
+
380
+ export const validate = (schema: AnyZodObject) => {
381
+ return async (req: Request, res: Response, next: NextFunction) => {
382
+ try {
383
+ await schema.parseAsync({
384
+ body: req.body,
385
+ query: req.query,
386
+ params: req.params,
387
+ });
388
+ next();
389
+ } catch (error) {
390
+ if (error instanceof ZodError) {
391
+ const errors = error.errors.map((err) => ({
392
+ field: err.path.join("."),
393
+ message: err.message,
394
+ }));
395
+ next(new ValidationError("Validation failed", errors));
396
+ } else {
397
+ next(error);
398
+ }
399
+ }
400
+ };
401
+ };
402
+
403
+ // Usage with Zod
404
+ import { z } from "zod";
405
+
406
+ const createUserSchema = z.object({
407
+ body: z.object({
408
+ name: z.string().min(1),
409
+ email: z.string().email(),
410
+ password: z.string().min(8),
411
+ }),
412
+ });
413
+
414
+ router.post("/users", validate(createUserSchema), userController.createUser);
415
+ ```
416
+
417
+ ### Rate Limiting Middleware
418
+
419
+ ```typescript
420
+ // middleware/rate-limit.middleware.ts
421
+ import rateLimit from "express-rate-limit";
422
+ import RedisStore from "rate-limit-redis";
423
+ import Redis from "ioredis";
424
+
425
+ const redis = new Redis({
426
+ host: process.env.REDIS_HOST,
427
+ port: parseInt(process.env.REDIS_PORT || "6379"),
428
+ });
429
+
430
+ export const apiLimiter = rateLimit({
431
+ store: new RedisStore({
432
+ client: redis,
433
+ prefix: "rl:",
434
+ }),
435
+ windowMs: 15 * 60 * 1000, // 15 minutes
436
+ max: 100, // Limit each IP to 100 requests per windowMs
437
+ message: "Too many requests from this IP, please try again later",
438
+ standardHeaders: true,
439
+ legacyHeaders: false,
440
+ });
441
+
442
+ export const authLimiter = rateLimit({
443
+ store: new RedisStore({
444
+ client: redis,
445
+ prefix: "rl:auth:",
446
+ }),
447
+ windowMs: 15 * 60 * 1000,
448
+ max: 5, // Stricter limit for auth endpoints
449
+ skipSuccessfulRequests: true,
450
+ });
451
+ ```
452
+
453
+ ### Request Logging Middleware
454
+
455
+ ```typescript
456
+ // middleware/logger.middleware.ts
457
+ import { Request, Response, NextFunction } from "express";
458
+ import pino from "pino";
459
+
460
+ const logger = pino({
461
+ level: process.env.LOG_LEVEL || "info",
462
+ transport: {
463
+ target: "pino-pretty",
464
+ options: { colorize: true },
465
+ },
466
+ });
467
+
468
+ export const requestLogger = (
469
+ req: Request,
470
+ res: Response,
471
+ next: NextFunction,
472
+ ) => {
473
+ const start = Date.now();
474
+
475
+ // Log response when finished
476
+ res.on("finish", () => {
477
+ const duration = Date.now() - start;
478
+ logger.info({
479
+ method: req.method,
480
+ url: req.url,
481
+ status: res.statusCode,
482
+ duration: `${duration}ms`,
483
+ userAgent: req.headers["user-agent"],
484
+ ip: req.ip,
485
+ });
486
+ });
487
+
488
+ next();
489
+ };
490
+
491
+ export { logger };
492
+ ```
493
+
494
+ ## Error Handling
495
+
496
+ ### Custom Error Classes
497
+
498
+ ```typescript
499
+ // utils/errors.ts
500
+ export class AppError extends Error {
501
+ constructor(
502
+ public message: string,
503
+ public statusCode: number = 500,
504
+ public isOperational: boolean = true,
505
+ ) {
506
+ super(message);
507
+ Object.setPrototypeOf(this, AppError.prototype);
508
+ Error.captureStackTrace(this, this.constructor);
509
+ }
510
+ }
511
+
512
+ export class ValidationError extends AppError {
513
+ constructor(
514
+ message: string,
515
+ public errors?: any[],
516
+ ) {
517
+ super(message, 400);
518
+ }
519
+ }
520
+
521
+ export class NotFoundError extends AppError {
522
+ constructor(message: string = "Resource not found") {
523
+ super(message, 404);
524
+ }
525
+ }
526
+
527
+ export class UnauthorizedError extends AppError {
528
+ constructor(message: string = "Unauthorized") {
529
+ super(message, 401);
530
+ }
531
+ }
532
+
533
+ export class ForbiddenError extends AppError {
534
+ constructor(message: string = "Forbidden") {
535
+ super(message, 403);
536
+ }
537
+ }
538
+
539
+ export class ConflictError extends AppError {
540
+ constructor(message: string) {
541
+ super(message, 409);
542
+ }
543
+ }
544
+ ```
545
+
546
+ ### Global Error Handler
547
+
548
+ ```typescript
549
+ // middleware/error-handler.ts
550
+ import { Request, Response, NextFunction } from "express";
551
+ import { AppError } from "../utils/errors";
552
+ import { logger } from "./logger.middleware";
553
+
554
+ export const errorHandler = (
555
+ err: Error,
556
+ req: Request,
557
+ res: Response,
558
+ next: NextFunction,
559
+ ) => {
560
+ if (err instanceof AppError) {
561
+ return res.status(err.statusCode).json({
562
+ status: "error",
563
+ message: err.message,
564
+ ...(err instanceof ValidationError && { errors: err.errors }),
565
+ });
566
+ }
567
+
568
+ // Log unexpected errors
569
+ logger.error({
570
+ error: err.message,
571
+ stack: err.stack,
572
+ url: req.url,
573
+ method: req.method,
574
+ });
575
+
576
+ // Don't leak error details in production
577
+ const message =
578
+ process.env.NODE_ENV === "production"
579
+ ? "Internal server error"
580
+ : err.message;
581
+
582
+ res.status(500).json({
583
+ status: "error",
584
+ message,
585
+ });
586
+ };
587
+
588
+ // Async error wrapper
589
+ export const asyncHandler = (
590
+ fn: (req: Request, res: Response, next: NextFunction) => Promise<any>,
591
+ ) => {
592
+ return (req: Request, res: Response, next: NextFunction) => {
593
+ Promise.resolve(fn(req, res, next)).catch(next);
594
+ };
595
+ };
596
+ ```
597
+
598
+ ## Database Patterns
599
+
600
+ Node.js supports both SQL and NoSQL databases. Use connection pooling for all production databases.
601
+
602
+ Key patterns covered in [references/advanced-patterns.md](references/advanced-patterns.md):
603
+ - **PostgreSQL with connection pool** — `pg` Pool configuration and graceful shutdown
604
+ - **MongoDB with Mongoose** — connection management and schema definition
605
+ - **Transaction pattern** — `BEGIN`/`COMMIT`/`ROLLBACK` with `pg` client
606
+
607
+ ## Authentication & Authorization
608
+
609
+ JWT-based auth with access tokens (short-lived, 15m) and refresh tokens (7d). Full `AuthService` implementation with `bcrypt` password comparison in [references/advanced-patterns.md](references/advanced-patterns.md).
610
+
611
+ ## Caching Strategies
612
+
613
+ Redis-backed `CacheService` with get/set/delete/invalidatePattern, plus a `@Cacheable` decorator for method-level caching. See [references/advanced-patterns.md](references/advanced-patterns.md).
614
+
615
+ ## API Response Format
616
+
617
+ Standardized `ApiResponse` helper with `success`, `error`, and `paginated` static methods. See [references/advanced-patterns.md](references/advanced-patterns.md).
618
+
619
+ ## Best Practices
620
+
621
+ 1. **Use TypeScript**: Type safety prevents runtime errors
622
+ 2. **Implement proper error handling**: Use custom error classes
623
+ 3. **Validate input**: Use libraries like Zod or Joi
624
+ 4. **Use environment variables**: Never hardcode secrets
625
+ 5. **Implement logging**: Use structured logging (Pino, Winston)
626
+ 6. **Add rate limiting**: Prevent abuse
627
+ 7. **Use HTTPS**: Always in production
628
+ 8. **Implement CORS properly**: Don't use `*` in production
629
+ 9. **Use dependency injection**: Easier testing and maintenance
630
+ 10. **Write tests**: Unit, integration, and E2E tests
631
+ 11. **Handle graceful shutdown**: Clean up resources
632
+ 12. **Use connection pooling**: For databases
633
+ 13. **Implement health checks**: For monitoring
634
+ 14. **Use compression**: Reduce response size
635
+ 15. **Monitor performance**: Use APM tools
636
+
637
+ ## Testing Patterns
638
+
639
+ See `javascript-testing-patterns` skill for comprehensive testing guidance.