create-tigra 1.0.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 (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -0
  3. package/bin/create-tigra.js +292 -0
  4. package/package.json +41 -0
  5. package/template/.agent/rules/client/01-project-structure.md +326 -0
  6. package/template/.agent/rules/client/02-component-patterns.md +249 -0
  7. package/template/.agent/rules/client/03-typescript-rules.md +226 -0
  8. package/template/.agent/rules/client/04-state-management.md +474 -0
  9. package/template/.agent/rules/client/05-api-integration.md +129 -0
  10. package/template/.agent/rules/client/06-forms-validation.md +129 -0
  11. package/template/.agent/rules/client/07-common-patterns.md +150 -0
  12. package/template/.agent/rules/client/08-color-system.md +93 -0
  13. package/template/.agent/rules/client/09-security-rules.md +97 -0
  14. package/template/.agent/rules/client/10-testing-strategy.md +370 -0
  15. package/template/.agent/rules/global/ai-edit-safety.md +38 -0
  16. package/template/.agent/rules/server/01-db-and-migrations.md +242 -0
  17. package/template/.agent/rules/server/02-general-rules.md +111 -0
  18. package/template/.agent/rules/server/03-migrations.md +20 -0
  19. package/template/.agent/rules/server/04-pagination.md +130 -0
  20. package/template/.agent/rules/server/05-project-conventions.md +71 -0
  21. package/template/.agent/rules/server/06-response-handling.md +173 -0
  22. package/template/.agent/rules/server/07-testing-strategy.md +506 -0
  23. package/template/.agent/rules/server/08-observability.md +180 -0
  24. package/template/.agent/rules/server/09-api-documentation-v2.md +168 -0
  25. package/template/.agent/rules/server/10-background-jobs-v2.md +185 -0
  26. package/template/.agent/rules/server/11-rate-limiting-v2.md +210 -0
  27. package/template/.agent/rules/server/12-performance-optimization.md +567 -0
  28. package/template/.claude/rules/client-01-project-structure.md +327 -0
  29. package/template/.claude/rules/client-02-component-patterns.md +250 -0
  30. package/template/.claude/rules/client-03-typescript-rules.md +227 -0
  31. package/template/.claude/rules/client-04-state-management.md +475 -0
  32. package/template/.claude/rules/client-05-api-integration.md +130 -0
  33. package/template/.claude/rules/client-06-forms-validation.md +130 -0
  34. package/template/.claude/rules/client-07-common-patterns.md +151 -0
  35. package/template/.claude/rules/client-08-color-system.md +94 -0
  36. package/template/.claude/rules/client-09-security-rules.md +98 -0
  37. package/template/.claude/rules/client-10-testing-strategy.md +371 -0
  38. package/template/.claude/rules/global-ai-edit-safety.md +39 -0
  39. package/template/.claude/rules/server-01-db-and-migrations.md +243 -0
  40. package/template/.claude/rules/server-02-general-rules.md +112 -0
  41. package/template/.claude/rules/server-03-migrations.md +21 -0
  42. package/template/.claude/rules/server-04-pagination.md +131 -0
  43. package/template/.claude/rules/server-05-project-conventions.md +72 -0
  44. package/template/.claude/rules/server-06-response-handling.md +174 -0
  45. package/template/.claude/rules/server-07-testing-strategy.md +507 -0
  46. package/template/.claude/rules/server-08-observability.md +181 -0
  47. package/template/.claude/rules/server-09-api-documentation-v2.md +169 -0
  48. package/template/.claude/rules/server-10-background-jobs-v2.md +186 -0
  49. package/template/.claude/rules/server-11-rate-limiting-v2.md +211 -0
  50. package/template/.claude/rules/server-12-performance-optimization.md +568 -0
  51. package/template/.cursor/rules/client-01-project-structure.mdc +327 -0
  52. package/template/.cursor/rules/client-02-component-patterns.mdc +250 -0
  53. package/template/.cursor/rules/client-03-typescript-rules.mdc +227 -0
  54. package/template/.cursor/rules/client-04-state-management.mdc +475 -0
  55. package/template/.cursor/rules/client-05-api-integration.mdc +130 -0
  56. package/template/.cursor/rules/client-06-forms-validation.mdc +130 -0
  57. package/template/.cursor/rules/client-07-common-patterns.mdc +151 -0
  58. package/template/.cursor/rules/client-08-color-system.mdc +94 -0
  59. package/template/.cursor/rules/client-09-security-rules.mdc +98 -0
  60. package/template/.cursor/rules/client-10-testing-strategy.mdc +371 -0
  61. package/template/.cursor/rules/global-ai-edit-safety.mdc +39 -0
  62. package/template/.cursor/rules/server-01-db-and-migrations.mdc +243 -0
  63. package/template/.cursor/rules/server-02-general-rules.mdc +112 -0
  64. package/template/.cursor/rules/server-03-migrations.mdc +21 -0
  65. package/template/.cursor/rules/server-04-pagination.mdc +131 -0
  66. package/template/.cursor/rules/server-05-project-conventions.mdc +72 -0
  67. package/template/.cursor/rules/server-06-response-handling.mdc +174 -0
  68. package/template/.cursor/rules/server-07-testing-strategy.mdc +507 -0
  69. package/template/.cursor/rules/server-08-observability.mdc +181 -0
  70. package/template/.cursor/rules/server-09-api-documentation-v2.mdc +169 -0
  71. package/template/.cursor/rules/server-10-background-jobs-v2.mdc +186 -0
  72. package/template/.cursor/rules/server-11-rate-limiting-v2.mdc +211 -0
  73. package/template/.cursor/rules/server-12-performance-optimization.mdc +568 -0
  74. package/template/CLAUDE.md +207 -0
  75. package/template/server/.env.example +148 -0
  76. package/template/server/.tsc-aliasrc.json +12 -0
  77. package/template/server/README.md +175 -0
  78. package/template/server/SECURITY.md +190 -0
  79. package/template/server/biome.json +42 -0
  80. package/template/server/docker-compose.yml +111 -0
  81. package/template/server/package.json +83 -0
  82. package/template/server/postman_collection.json +733 -0
  83. package/template/server/prisma/schema.prisma +92 -0
  84. package/template/server/prisma/seed.ts +142 -0
  85. package/template/server/scripts/wait-for-db.js +60 -0
  86. package/template/server/src/app.ts +74 -0
  87. package/template/server/src/config/env.ts +101 -0
  88. package/template/server/src/hooks/request-timing.hook.ts +26 -0
  89. package/template/server/src/libs/auth/authenticate.middleware.ts +22 -0
  90. package/template/server/src/libs/auth/rbac.middleware.test.ts +134 -0
  91. package/template/server/src/libs/auth/rbac.middleware.ts +147 -0
  92. package/template/server/src/libs/db.ts +76 -0
  93. package/template/server/src/libs/error-handler.ts +89 -0
  94. package/template/server/src/libs/logger.ts +60 -0
  95. package/template/server/src/libs/queue.ts +79 -0
  96. package/template/server/src/libs/redis.ts +79 -0
  97. package/template/server/src/libs/swagger-schemas.ts +16 -0
  98. package/template/server/src/modules/admin/admin.controller.ts +122 -0
  99. package/template/server/src/modules/admin/admin.routes.ts +100 -0
  100. package/template/server/src/modules/admin/admin.schemas.ts +35 -0
  101. package/template/server/src/modules/admin/admin.service.ts +167 -0
  102. package/template/server/src/modules/auth/auth.controller.ts +141 -0
  103. package/template/server/src/modules/auth/auth.integration.test.ts +150 -0
  104. package/template/server/src/modules/auth/auth.repo.ts +218 -0
  105. package/template/server/src/modules/auth/auth.routes.ts +204 -0
  106. package/template/server/src/modules/auth/auth.schemas.ts +137 -0
  107. package/template/server/src/modules/auth/auth.service.test.ts +119 -0
  108. package/template/server/src/modules/auth/auth.service.ts +329 -0
  109. package/template/server/src/modules/auth/auth.types.ts +97 -0
  110. package/template/server/src/modules/resources/resources.controller.ts +218 -0
  111. package/template/server/src/modules/resources/resources.repo.ts +253 -0
  112. package/template/server/src/modules/resources/resources.routes.ts +355 -0
  113. package/template/server/src/modules/resources/resources.schemas.ts +146 -0
  114. package/template/server/src/modules/resources/resources.service.ts +218 -0
  115. package/template/server/src/modules/resources/resources.types.ts +73 -0
  116. package/template/server/src/plugins/rate-limit.plugin.ts +21 -0
  117. package/template/server/src/plugins/security.plugin.ts +21 -0
  118. package/template/server/src/plugins/swagger.plugin.ts +41 -0
  119. package/template/server/src/routes/health.routes.ts +31 -0
  120. package/template/server/src/server.ts +142 -0
  121. package/template/server/src/test/setup.ts +38 -0
  122. package/template/server/src/types/fastify.d.ts +36 -0
  123. package/template/server/src/utils/errors.ts +108 -0
  124. package/template/server/src/utils/pagination.ts +120 -0
  125. package/template/server/src/utils/response.ts +110 -0
  126. package/template/server/src/workers/file.worker.ts +106 -0
  127. package/template/server/tsconfig.build.json +30 -0
  128. package/template/server/tsconfig.build.tsbuildinfo +1 -0
  129. package/template/server/tsconfig.json +89 -0
  130. package/template/server/tsconfig.test.json +22 -0
  131. package/template/server/vitest.config.ts +98 -0
@@ -0,0 +1,568 @@
1
+ ---
2
+ trigger: always_on
3
+ globs: "server/**/*"
4
+ ---
5
+
6
+ > **SCOPE**: These rules apply specifically to the **server** directory.
7
+
8
+ # Performance Optimization
9
+
10
+ ## Core Principles
11
+
12
+ - **Measure first, optimize second** - Use profiling to find actual bottlenecks
13
+ - **Database queries** are the #1 performance killer
14
+ - **Caching** can eliminate 80% of database queries
15
+ - **Async everything** - Never block the event loop
16
+
17
+ ---
18
+
19
+ ## Database Query Optimization
20
+
21
+ ### 1. Prevent N+1 Queries
22
+
23
+ ```typescript
24
+ // ❌ BAD - N+1 query problem
25
+ async function getResourcesWithOwners() {
26
+ const resources = await prisma.resource.findMany();
27
+
28
+ // This executes 1 query per resource!
29
+ for (const resource of resources) {
30
+ resource.owner = await prisma.user.findUnique({
31
+ where: { id: resource.ownerId },
32
+ });
33
+ }
34
+
35
+ return resources;
36
+ }
37
+
38
+ // ✅ GOOD - Single query with join
39
+ async function getResourcesWithOwners() {
40
+ return await prisma.resource.findMany({
41
+ include: {
42
+ owner: true, // Prisma joins in one query
43
+ },
44
+ });
45
+ }
46
+ ```
47
+
48
+ ### 2. Select Only Needed Fields
49
+
50
+ ```typescript
51
+ // ❌ BAD - Fetches all columns
52
+ const users = await prisma.user.findMany();
53
+
54
+ // ✅ GOOD - Select specific fields
55
+ const users = await prisma.user.findMany({
56
+ select: {
57
+ id: true,
58
+ email: true,
59
+ name: true,
60
+ // Don't fetch password, createdAt, etc.
61
+ },
62
+ });
63
+ ```
64
+
65
+ ### 3. Use Pagination
66
+
67
+ ```typescript
68
+ // ❌ BAD - Loads all records into memory
69
+ const resources = await prisma.resource.findMany();
70
+
71
+ // ✅ GOOD - Paginate
72
+ const resources = await prisma.resource.findMany({
73
+ take: limit,
74
+ skip: (page - 1) * limit,
75
+ });
76
+ ```
77
+
78
+ ### 4. Index Frequently Queried Fields
79
+
80
+ ```prisma
81
+ // schema.prisma
82
+ model Resource {
83
+ id String @id @default(uuid())
84
+ ownerId String
85
+ status String
86
+ price Float
87
+ category String
88
+ createdAt DateTime @default(now())
89
+
90
+ // Add indexes for fields used in WHERE clauses
91
+ @@index([ownerId])
92
+ @@index([status])
93
+ @@index([category])
94
+ @@index([createdAt])
95
+
96
+ // Composite index for common queries
97
+ @@index([category, status, createdAt])
98
+ }
99
+ ```
100
+
101
+ ### 5. Use Transactions Sparingly
102
+
103
+ ```typescript
104
+ // ❌ BAD - Unnecessary transaction
105
+ await prisma.$transaction([
106
+ prisma.user.findUnique({ where: { id: '1' } }), // Just a read!
107
+ ]);
108
+
109
+ // ✅ GOOD - Transaction only for writes
110
+ await prisma.$transaction([
111
+ prisma.user.update({ where: { id: '1' }, data: { credits: { decrement: 10 } } }),
112
+ prisma.payment.create({ data: { userId: '1', amount: 10 } }),
113
+ ]);
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Redis Caching Strategies
119
+
120
+ ### 1. Cache Expensive Queries
121
+
122
+ ```typescript
123
+ // libs/cache.ts
124
+ import { redis } from './redis';
125
+
126
+ export async function getCachedOrFetch<T>(
127
+ key: string,
128
+ fetchFn: () => Promise<T>,
129
+ ttl = 300 // 5 minutes
130
+ ): Promise<T> {
131
+ // Try cache first
132
+ const cached = await redis.get(key);
133
+ if (cached) {
134
+ return JSON.parse(cached);
135
+ }
136
+
137
+ // Fetch from DB
138
+ const data = await fetchFn();
139
+
140
+ // Store in cache
141
+ await redis.setex(key, ttl, JSON.stringify(data));
142
+
143
+ return data;
144
+ }
145
+ ```
146
+
147
+ ```typescript
148
+ // Usage in service
149
+ async function getPopularResources() {
150
+ return getCachedOrFetch(
151
+ 'popular-resources',
152
+ async () => {
153
+ return await prisma.resource.findMany({
154
+ where: { isPopular: true },
155
+ take: 10,
156
+ });
157
+ },
158
+ 600 // Cache for 10 minutes
159
+ );
160
+ }
161
+ ```
162
+
163
+ ### 2. Cache Invalidation
164
+
165
+ ```typescript
166
+ // When resource is updated, invalidate cache
167
+ async function updateResource(id: string, data: any) {
168
+ const updated = await prisma.resource.update({
169
+ where: { id },
170
+ data,
171
+ });
172
+
173
+ // Invalidate relevant caches
174
+ await redis.del(`resource:${id}`);
175
+ await redis.del('popular-resources');
176
+ await redis.del('recent-resources');
177
+
178
+ return updated;
179
+ }
180
+ ```
181
+
182
+ ### 3. Cache Patterns
183
+
184
+ ```typescript
185
+ // Pattern: Cache-Aside (Lazy Loading)
186
+ async function getResource(id: string) {
187
+ const cacheKey = `resource:${id}`;
188
+ const cached = await redis.get(cacheKey);
189
+
190
+ if (cached) return JSON.parse(cached);
191
+
192
+ const resource = await prisma.resource.findUnique({ where: { id } });
193
+ if (resource) {
194
+ await redis.setex(cacheKey, 300, JSON.stringify(resource));
195
+ }
196
+
197
+ return resource;
198
+ }
199
+
200
+ // Pattern: Write-Through
201
+ async function createResource(data: CreateResourceInput) {
202
+ const resource = await prisma.resource.create({ data });
203
+
204
+ // Immediately cache new resource
205
+ await redis.setex(
206
+ `resource:${resource.id}`,
207
+ 300,
208
+ JSON.stringify(resource)
209
+ );
210
+
211
+ return resource;
212
+ }
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Query Performance Monitoring
218
+
219
+ ### 1. Log Slow Queries
220
+
221
+ ```typescript
222
+ // libs/db.ts (already in observability.md)
223
+ prisma.$on('query', (e) => {
224
+ if (e.duration > 1000) {
225
+ logger.warn(
226
+ {
227
+ query: e.query,
228
+ duration: e.duration,
229
+ params: e.params,
230
+ },
231
+ 'Slow query detected'
232
+ );
233
+ }
234
+ });
235
+ ```
236
+
237
+ ### 2. Database Connection Pooling
238
+
239
+ ```typescript
240
+ // Prisma auto-manages connection pool
241
+ // Configure in DATABASE_URL
242
+ // mysql://user:pass@localhost:3306/db?connection_limit=10
243
+ ```
244
+
245
+ ---
246
+
247
+ ## Response Compression
248
+
249
+ ```typescript
250
+ // app.ts
251
+ import compress from '@fastify/compress';
252
+
253
+ await app.register(compress, {
254
+ global: true,
255
+ threshold: 1024, // Only compress responses > 1KB
256
+ encodings: ['gzip', 'deflate'],
257
+ });
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Async Processing Best Practices
263
+
264
+ ### 1. Use Background Jobs for Slow Operations
265
+
266
+ ```typescript
267
+ // ❌ BAD - Blocks HTTP response
268
+ app.post('/resources', async (request, reply) => {
269
+ const resource = await createResource(request.body);
270
+
271
+ // This takes 5 seconds!
272
+ await sendEmailToOwner(resource.ownerId);
273
+ await generateThumbnails(resource.images);
274
+ await notifyFollowers(resource.ownerId);
275
+
276
+ return reply.send(resource);
277
+ });
278
+
279
+ // ✅ GOOD - Offload to background jobs
280
+ app.post('/resources', async (request, reply) => {
281
+ const resource = await createResource(request.body);
282
+
283
+ // Queue background jobs (non-blocking)
284
+ await emailQueue.add('notify-owner', { resourceId: resource.id });
285
+ await fileQueue.add('generate-thumbnails', { resourceId: resource.id });
286
+ await notificationQueue.add('notify-followers', { ownerId: resource.ownerId });
287
+
288
+ // Return immediately
289
+ return reply.send(resource);
290
+ });
291
+ ```
292
+
293
+ ### 2. Parallel Execution
294
+
295
+ ```typescript
296
+ // ❌ BAD - Sequential (slow)
297
+ const user = await prisma.user.findUnique({ where: { id } });
298
+ const resources = await prisma.resource.findMany({ where: { ownerId: id } });
299
+ const stats = await calculateUserStats(id);
300
+
301
+ // ✅ GOOD - Parallel (fast)
302
+ const [user, resources, stats] = await Promise.all([
303
+ prisma.user.findUnique({ where: { id } }),
304
+ prisma.resource.findMany({ where: { ownerId: id } }),
305
+ calculateUserStats(id),
306
+ ]);
307
+ ```
308
+
309
+ ---
310
+
311
+ ## Response Optimization
312
+
313
+ ### 1. Minimize Response Size
314
+
315
+ ```typescript
316
+ // ❌ BAD - Returns entire objects
317
+ app.get('/resources', async (request, reply) => {
318
+ const resources = await prisma.resource.findMany();
319
+ return resources; // Includes unnecessary fields
320
+ });
321
+
322
+ // ✅ GOOD - Select only needed fields
323
+ app.get('/resources', async (request, reply) => {
324
+ const resources = await prisma.resource.findMany({
325
+ select: {
326
+ id: true,
327
+ title: true,
328
+ price: true,
329
+ thumbnailUrl: true,
330
+ // Don't include description, metadata, etc. in list view
331
+ },
332
+ });
333
+ return resources;
334
+ });
335
+ ```
336
+
337
+ ### 2. Use Streaming for Large Responses
338
+
339
+ ```typescript
340
+ // For large datasets, stream instead of buffering
341
+ app.get('/export/resources', async (request, reply) => {
342
+ reply.type('application/json');
343
+
344
+ const stream = prisma.resource.findMany({ stream: true });
345
+
346
+ return reply.send(stream);
347
+ });
348
+ ```
349
+
350
+ ---
351
+
352
+ ## Memory Management
353
+
354
+ ### 1. Avoid Memory Leaks
355
+
356
+ ```typescript
357
+ // ❌ BAD - Memory leak
358
+ const cache = new Map();
359
+
360
+ app.get('/data', (request, reply) => {
361
+ const data = getExpensiveData();
362
+ cache.set(request.id, data); // Never cleaned up!
363
+ return data;
364
+ });
365
+
366
+ // ✅ GOOD - Use Redis or LRU cache
367
+ import LRU from 'lru-cache';
368
+
369
+ const cache = new LRU({
370
+ max: 500, // Max 500 items
371
+ ttl: 1000 * 60 * 5, // 5 minutes
372
+ });
373
+ ```
374
+
375
+ ### 2. Batch Processing
376
+
377
+ ```typescript
378
+ // ❌ BAD - Process one by one
379
+ for (const user of users) {
380
+ await sendEmail(user.email);
381
+ }
382
+
383
+ // ✅ GOOD - Batch process
384
+ const chunks = chunkArray(users, 100);
385
+ for (const chunk of chunks) {
386
+ await Promise.all(chunk.map(user => sendEmail(user.email)));
387
+ }
388
+ ```
389
+
390
+ ---
391
+
392
+ ## API Design for Performance
393
+
394
+ ### 1. Cursor-Based Pagination (Large Datasets)
395
+
396
+ ```typescript
397
+ // Better than offset pagination for large datasets
398
+ app.get('/resources', async (request, reply) => {
399
+ const { cursor, limit = 10 } = request.query;
400
+
401
+ const resources = await prisma.resource.findMany({
402
+ take: limit + 1, // Fetch one extra to check if more exist
403
+ cursor: cursor ? { id: cursor } : undefined,
404
+ orderBy: { createdAt: 'desc' },
405
+ });
406
+
407
+ const hasMore = resources.length > limit;
408
+ const items = hasMore ? resources.slice(0, -1) : resources;
409
+
410
+ return {
411
+ items,
412
+ nextCursor: hasMore ? items[items.length - 1].id : null,
413
+ };
414
+ });
415
+ ```
416
+
417
+ ### 2. Field Selection (GraphQL-style)
418
+
419
+ ```typescript
420
+ // Allow clients to request only needed fields
421
+ app.get('/users/:id', async (request, reply) => {
422
+ const { fields } = request.query; // ?fields=id,email,name
423
+
424
+ const select = fields
425
+ ? fields.split(',').reduce((acc, field) => {
426
+ acc[field] = true;
427
+ return acc;
428
+ }, {})
429
+ : undefined;
430
+
431
+ const user = await prisma.user.findUnique({
432
+ where: { id: request.params.id },
433
+ select,
434
+ });
435
+
436
+ return user;
437
+ });
438
+ ```
439
+
440
+ ---
441
+
442
+ ## Load Testing
443
+
444
+ ```bash
445
+ # Install k6 for load testing
446
+ brew install k6 # macOS
447
+ # or
448
+ choco install k6 # Windows
449
+
450
+ # Create load test script
451
+ cat > load-test.js << 'EOF'
452
+ import http from 'k6/http';
453
+ import { check } from 'k6';
454
+
455
+ export const options = {
456
+ stages: [
457
+ { duration: '30s', target: 50 }, // Ramp up to 50 users
458
+ { duration: '1m', target: 100 }, // Stay at 100 users
459
+ { duration: '30s', target: 0 }, // Ramp down
460
+ ],
461
+ thresholds: {
462
+ http_req_duration: ['p(95)<500'], // 95% of requests < 500ms
463
+ },
464
+ };
465
+
466
+ export default function () {
467
+ const res = http.get('http://localhost:3000/api/v1/resources');
468
+ check(res, {
469
+ 'status is 200': (r) => r.status === 200,
470
+ 'response time < 500ms': (r) => r.timings.duration < 500,
471
+ });
472
+ }
473
+ EOF
474
+
475
+ # Run test
476
+ k6 run load-test.js
477
+ ```
478
+
479
+ ---
480
+
481
+ ## Performance Monitoring
482
+
483
+ ### 1. Response Time Tracking
484
+
485
+ ```typescript
486
+ // Already covered in observability.md
487
+ app.addHook('onRequest', (request, reply, done) => {
488
+ request.startTime = Date.now();
489
+ done();
490
+ });
491
+
492
+ app.addHook('onResponse', (request, reply, done) => {
493
+ const duration = Date.now() - request.startTime;
494
+
495
+ logger.info({
496
+ method: request.method,
497
+ url: request.url,
498
+ statusCode: reply.statusCode,
499
+ duration,
500
+ });
501
+
502
+ done();
503
+ });
504
+ ```
505
+
506
+ ### 2. Database Query Metrics
507
+
508
+ ```typescript
509
+ let queryCount = 0;
510
+ let totalQueryTime = 0;
511
+
512
+ prisma.$on('query', (e) => {
513
+ queryCount++;
514
+ totalQueryTime += e.duration;
515
+ });
516
+
517
+ app.get('/metrics/db', async (request, reply) => {
518
+ return {
519
+ totalQueries: queryCount,
520
+ avgDuration: totalQueryTime / queryCount,
521
+ };
522
+ });
523
+ ```
524
+
525
+ ---
526
+
527
+ ## Performance Checklist
528
+
529
+ ### Database
530
+ - [ ] Indexes on frequently queried fields
531
+ - [ ] No N+1 queries
532
+ - [ ] Pagination implemented
533
+ - [ ] Select only needed fields
534
+ - [ ] Connection pooling configured
535
+ - [ ] Slow query logging enabled
536
+
537
+ ### Caching
538
+ - [ ] Redis caching for expensive queries
539
+ - [ ] Cache invalidation strategy
540
+ - [ ] TTL set appropriately
541
+ - [ ] Cache hit/miss monitoring
542
+
543
+ ### API
544
+ - [ ] Response compression enabled
545
+ - [ ] Background jobs for slow operations
546
+ - [ ] Parallel execution where possible
547
+ - [ ] Minimal response payloads
548
+ - [ ] Rate limiting configured
549
+
550
+ ### Monitoring
551
+ - [ ] Response time tracking
552
+ - [ ] Database metrics collected
553
+ - [ ] Load testing performed
554
+ - [ ] Error rates monitored
555
+ - [ ] Memory usage monitored
556
+
557
+ ---
558
+
559
+ ## Performance Targets
560
+
561
+ | Metric | Target |
562
+ |--------|--------|
563
+ | API Response Time (p95) | < 500ms |
564
+ | API Response Time (p99) | < 1s |
565
+ | Database Query Time (p95) | < 100ms |
566
+ | Cache Hit Rate | > 80% |
567
+ | Error Rate | < 0.1% |
568
+ | Throughput | > 1000 req/s |