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,169 @@
1
+ ---
2
+ trigger: always_on
3
+ globs: "server/**/*"
4
+ ---
5
+
6
+ > **SCOPE**: These rules apply specifically to the **server** directory.
7
+
8
+ # API Documentation
9
+
10
+ ## Stack
11
+
12
+ ```json
13
+ {
14
+ "dependencies": {
15
+ "@fastify/swagger": "^8.12.0",
16
+ "@fastify/swagger-ui": "^2.0.0",
17
+ "zod-to-json-schema": "^3.22.0"
18
+ }
19
+ }
20
+ ```
21
+
22
+ ## Setup
23
+
24
+ ```typescript
25
+ // app.ts
26
+ import swagger from '@fastify/swagger';
27
+ import swaggerUI from '@fastify/swagger-ui';
28
+
29
+ await app.register(swagger, {
30
+ openapi: {
31
+ info: {
32
+ title: 'API Server',
33
+ version: '1.0.0',
34
+ },
35
+ servers: [
36
+ { url: 'http://localhost:3000', description: 'Development' },
37
+ ],
38
+ tags: [
39
+ { name: 'auth', description: 'Authentication' },
40
+ { name: 'resources', description: 'Resource operations' },
41
+ ],
42
+ components: {
43
+ securitySchemes: {
44
+ bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
45
+ },
46
+ },
47
+ },
48
+ });
49
+
50
+ await app.register(swaggerUI, {
51
+ routePrefix: '/docs',
52
+ uiConfig: { docExpansion: 'list', deepLinking: true },
53
+ });
54
+ ```
55
+
56
+ **Access:** `http://localhost:3000/docs`
57
+
58
+ ## Route Documentation Pattern
59
+
60
+ ```typescript
61
+ // modules/resources/resources.schemas.ts
62
+ import { z } from 'zod';
63
+
64
+ export const CreateResourceSchema = z.object({
65
+ title: z.string().min(1).max(200),
66
+ price: z.number().positive(),
67
+ });
68
+
69
+ export const ResourceResponseSchema = z.object({
70
+ success: z.literal(true),
71
+ message: z.string(),
72
+ data: z.object({
73
+ id: z.string().uuid(),
74
+ title: z.string(),
75
+ price: z.number(),
76
+ }),
77
+ });
78
+ ```
79
+
80
+ ```typescript
81
+ // modules/resources/resources.routes.ts
82
+ import { zodToJsonSchema } from 'zod-to-json-schema';
83
+ import * as schemas from './resources.schemas';
84
+
85
+ app.post('/resources', {
86
+ schema: {
87
+ description: 'Create a new resource',
88
+ tags: ['resources'],
89
+ summary: 'Create resource',
90
+ body: zodToJsonSchema(schemas.CreateResourceSchema, 'CreateResource'),
91
+ response: {
92
+ 201: zodToJsonSchema(schemas.ResourceResponseSchema, 'ResourceResponse'),
93
+ 400: {
94
+ description: 'Validation error',
95
+ type: 'object',
96
+ properties: {
97
+ success: { type: 'boolean', enum: [false] },
98
+ error: {
99
+ type: 'object',
100
+ properties: {
101
+ code: { type: 'string' },
102
+ message: { type: 'string' },
103
+ },
104
+ },
105
+ },
106
+ },
107
+ },
108
+ security: [{ bearerAuth: [] }],
109
+ },
110
+ handler: resourceController.createResource,
111
+ });
112
+ ```
113
+
114
+ ## Document All Responses
115
+
116
+ ```typescript
117
+ response: {
118
+ 200: successSchema,
119
+ 400: validationErrorSchema,
120
+ 401: unauthorizedSchema,
121
+ 404: notFoundSchema,
122
+ 500: internalErrorSchema,
123
+ }
124
+ ```
125
+
126
+ ## Production Config
127
+
128
+ ```typescript
129
+ if (process.env.NODE_ENV === 'production') {
130
+ // Option 1: Disable docs
131
+ // Don't register swagger-ui
132
+
133
+ // Option 2: Require auth for docs
134
+ await app.register(swaggerUI, {
135
+ routePrefix: '/docs',
136
+ transformSpecification: (swaggerObject, request, reply) => {
137
+ if (!request.headers.authorization) {
138
+ reply.code(401).send({ error: 'Unauthorized' });
139
+ }
140
+ return swaggerObject;
141
+ },
142
+ });
143
+ }
144
+ ```
145
+
146
+ ## Best Practices
147
+
148
+ ### ✅ DO:
149
+ - Use Zod schemas for all routes
150
+ - Document all error responses
151
+ - Add descriptions and summaries
152
+ - Include security requirements
153
+ - Tag routes by domain
154
+
155
+ ### ❌ DON'T:
156
+ - Skip documentation on routes
157
+ - Forget error response schemas
158
+ - Expose docs publicly in production
159
+ - Use generic descriptions
160
+
161
+ ## Checklist
162
+
163
+ - [ ] Swagger and Swagger UI registered
164
+ - [ ] All routes have schemas
165
+ - [ ] Zod schemas converted to JSON Schema
166
+ - [ ] Error responses documented
167
+ - [ ] Tags used for grouping
168
+ - [ ] Security requirements documented
169
+ - [ ] Production docs secured
@@ -0,0 +1,186 @@
1
+ ---
2
+ trigger: always_on
3
+ globs: "server/**/*"
4
+ ---
5
+
6
+ > **SCOPE**: These rules apply specifically to the **server** directory.
7
+
8
+ # Background Jobs & Queue Processing
9
+
10
+ ## Stack
11
+
12
+ ```json
13
+ {
14
+ "dependencies": {
15
+ "bullmq": "^5.0.0",
16
+ "ioredis": "^5.3.0"
17
+ }
18
+ }
19
+ ```
20
+
21
+ ## Redis Setup
22
+
23
+ ```typescript
24
+ // libs/redis.ts
25
+ import Redis from 'ioredis';
26
+
27
+ export const redis = new Redis({
28
+ host: process.env.REDIS_HOST || 'localhost',
29
+ port: parseInt(process.env.REDIS_PORT || '6379'),
30
+ maxRetriesPerRequest: null, // Required for BullMQ
31
+ });
32
+ ```
33
+
34
+ ## Queue Setup
35
+
36
+ ```typescript
37
+ // libs/queue.ts
38
+ import { Queue, Worker, QueueEvents } from 'bullmq';
39
+ import { redis } from './redis';
40
+
41
+ export const emailQueue = new Queue('emails', {
42
+ connection: redis,
43
+ defaultJobOptions: {
44
+ attempts: 3,
45
+ backoff: { type: 'exponential', delay: 2000 },
46
+ removeOnComplete: { age: 3600, count: 100 },
47
+ removeOnFail: { age: 86400 },
48
+ },
49
+ });
50
+ ```
51
+
52
+ ## Worker Pattern
53
+
54
+ ```typescript
55
+ // workers/email.worker.ts
56
+ import { Worker, Job } from 'bullmq';
57
+
58
+ interface EmailJobData {
59
+ to: string;
60
+ subject: string;
61
+ body: string;
62
+ }
63
+
64
+ export const emailWorker = new Worker<EmailJobData>(
65
+ 'emails',
66
+ async (job: Job<EmailJobData>) => {
67
+ const { to, subject, body } = job.data;
68
+
69
+ logger.info({ jobId: job.id, to }, 'Processing email');
70
+
71
+ await sendEmail({ to, subject, body });
72
+
73
+ return { success: true, sentAt: new Date().toISOString() };
74
+ },
75
+ {
76
+ connection: redis,
77
+ concurrency: 5,
78
+ limiter: { max: 100, duration: 60000 }, // 100 jobs/minute
79
+ }
80
+ );
81
+ ```
82
+
83
+ ## Job Patterns
84
+
85
+ ### 1. Immediate Background Job
86
+ ```typescript
87
+ // Add to queue (non-blocking)
88
+ await emailQueue.add('welcome-email', {
89
+ to: user.email,
90
+ subject: 'Welcome!',
91
+ body: 'Thanks for signing up',
92
+ });
93
+ ```
94
+
95
+ ### 2. Delayed Job
96
+ ```typescript
97
+ // Send after 24 hours
98
+ await emailQueue.add(
99
+ 'reminder-email',
100
+ { to: user.email, subject: 'Reminder', body: 'Complete your profile' },
101
+ { delay: 24 * 60 * 60 * 1000 }
102
+ );
103
+ ```
104
+
105
+ ### 3. Scheduled Job (Cron)
106
+ ```typescript
107
+ // Daily at 2 AM
108
+ await cleanupQueue.add(
109
+ 'daily-cleanup',
110
+ {},
111
+ { repeat: { pattern: '0 2 * * *' } }
112
+ );
113
+ ```
114
+
115
+ ### 4. Priority Job
116
+ ```typescript
117
+ await paymentQueue.add(
118
+ 'process-payment',
119
+ { orderId, amount },
120
+ { priority: 1 } // Lower = higher priority
121
+ );
122
+ ```
123
+
124
+ ## Error Handling
125
+
126
+ ```typescript
127
+ emailWorker.on('failed', (job, error) => {
128
+ logger.error({ jobId: job?.id, error }, 'Job failed');
129
+
130
+ // Move to dead letter queue after all retries
131
+ if (job && job.attemptsMade >= job.opts.attempts!) {
132
+ deadLetterQueue.add('failed-email', {
133
+ originalJob: job.data,
134
+ failedReason: error.message,
135
+ });
136
+ }
137
+ });
138
+ ```
139
+
140
+ ## Graceful Shutdown
141
+
142
+ ```typescript
143
+ // server.ts
144
+ const workers = [emailWorker, fileWorker];
145
+
146
+ async function gracefulShutdown() {
147
+ logger.info('Shutting down workers...');
148
+ await Promise.all(workers.map((w) => w.close()));
149
+ process.exit(0);
150
+ }
151
+
152
+ process.on('SIGTERM', gracefulShutdown);
153
+ process.on('SIGINT', gracefulShutdown);
154
+ ```
155
+
156
+ ## Use Cases
157
+
158
+ - **Email sending** (welcome, verification, notifications)
159
+ - **File processing** (image thumbnails, video encoding)
160
+ - **Report generation** (PDFs, exports)
161
+ - **Data cleanup** (expired records, temp files)
162
+ - **External API calls** (webhooks, third-party sync)
163
+
164
+ ## Best Practices
165
+
166
+ ### ✅ DO:
167
+ - Use background jobs for slow operations (>1s)
168
+ - Set appropriate retry strategies
169
+ - Implement rate limiting for external APIs
170
+ - Monitor job failures
171
+ - Clean up old jobs
172
+
173
+ ### ❌ DON'T:
174
+ - Block HTTP responses with heavy processing
175
+ - Forget error handling
176
+ - Skip monitoring
177
+ - Use queues for everything (simple tasks can run inline)
178
+
179
+ ## Checklist
180
+
181
+ - [ ] BullMQ and Redis installed
182
+ - [ ] Queues created with retry config
183
+ - [ ] Workers implemented with error handling
184
+ - [ ] Graceful shutdown configured
185
+ - [ ] Job monitoring in place
186
+ - [ ] Dead letter queue for failures
@@ -0,0 +1,211 @@
1
+ ---
2
+ trigger: always_on
3
+ globs: "server/**/*"
4
+ ---
5
+
6
+ > **SCOPE**: These rules apply specifically to the **server** directory.
7
+
8
+ # Rate Limiting & Throttling
9
+
10
+ ## Stack
11
+
12
+ ```json
13
+ {
14
+ "dependencies": {
15
+ "@fastify/rate-limit": "^9.0.0",
16
+ "ioredis": "^5.3.0"
17
+ }
18
+ }
19
+ ```
20
+
21
+ ## Basic Setup
22
+
23
+ ```typescript
24
+ // app.ts
25
+ import rateLimit from '@fastify/rate-limit';
26
+ import { redis } from './libs/redis';
27
+
28
+ await app.register(rateLimit, {
29
+ global: true,
30
+ max: 100,
31
+ timeWindow: '15 minutes',
32
+ redis,
33
+ nameSpace: 'rate-limit:',
34
+ addHeaders: {
35
+ 'x-ratelimit-limit': true,
36
+ 'x-ratelimit-remaining': true,
37
+ 'x-ratelimit-reset': true,
38
+ 'retry-after': true,
39
+ },
40
+ errorResponseBuilder: (request, context) => ({
41
+ success: false,
42
+ error: {
43
+ code: 'RATE_LIMIT_EXCEEDED',
44
+ message: `Rate limit exceeded. Retry in ${Math.ceil(context.ttl / 1000)}s.`,
45
+ },
46
+ }),
47
+ });
48
+ ```
49
+
50
+ ## Per-Route Limits
51
+
52
+ ```typescript
53
+ // Disable global, configure per route
54
+ await app.register(rateLimit, { global: false, redis });
55
+
56
+ // Strict limit for login
57
+ app.post('/auth/login', {
58
+ config: {
59
+ rateLimit: {
60
+ max: 5,
61
+ timeWindow: '15 minutes',
62
+ },
63
+ },
64
+ handler: authController.login,
65
+ });
66
+
67
+ // Generous limit for authenticated routes
68
+ app.get('/resources', {
69
+ preHandler: [app.authenticate],
70
+ config: {
71
+ rateLimit: {
72
+ max: 1000,
73
+ timeWindow: '15 minutes',
74
+ },
75
+ },
76
+ handler: resourceController.listResources,
77
+ });
78
+ ```
79
+
80
+ ## Custom Rate Limit Keys
81
+
82
+ ```typescript
83
+ await app.register(rateLimit, {
84
+ redis,
85
+ keyGenerator: (request) => {
86
+ // Authenticated: rate limit by user ID
87
+ if (request.user) {
88
+ return `user:${request.user.id}`;
89
+ }
90
+ // Anonymous: rate limit by IP
91
+ return `ip:${request.ip}`;
92
+ },
93
+ });
94
+ ```
95
+
96
+ ## User-Based Tiered Limits
97
+
98
+ ```typescript
99
+ await app.register(rateLimit, {
100
+ redis,
101
+ max: async (request) => {
102
+ if (!request.user) return 100; // Anonymous
103
+ if (request.user.role === 'ADMIN') return 10000;
104
+ if (request.user.tier === 'PREMIUM') return 5000;
105
+ return 1000; // Standard
106
+ },
107
+ });
108
+ ```
109
+
110
+ ## Rate Limit Tiers
111
+
112
+ ```typescript
113
+ // lib/constants/rate-limits.ts
114
+ export const RATE_LIMITS = {
115
+ LOGIN: { max: 5, timeWindow: '15 minutes' },
116
+ REGISTER: { max: 3, timeWindow: '1 hour' },
117
+ PASSWORD_RESET: { max: 3, timeWindow: '1 hour' },
118
+ PUBLIC: { max: 100, timeWindow: '15 minutes' },
119
+ AUTHENTICATED: { max: 1000, timeWindow: '15 minutes' },
120
+ PREMIUM: { max: 5000, timeWindow: '15 minutes' },
121
+ ADMIN: { max: 10000, timeWindow: '15 minutes' },
122
+ FILE_UPLOAD: { max: 10, timeWindow: '1 hour' },
123
+ } as const;
124
+ ```
125
+
126
+ ## Monitoring
127
+
128
+ ```typescript
129
+ await app.register(rateLimit, {
130
+ redis,
131
+ onExceeding: (request, key) => {
132
+ logger.warn({ key, ip: request.ip, url: request.url }, 'Approaching limit');
133
+ },
134
+ onExceeded: (request, key) => {
135
+ logger.error({ key, ip: request.ip, url: request.url }, 'Limit exceeded');
136
+ },
137
+ });
138
+ ```
139
+
140
+ ## Whitelist IPs
141
+
142
+ ```typescript
143
+ app.addHook('preHandler', async (request, reply) => {
144
+ const whitelistedIPs = ['127.0.0.1', '::1'];
145
+ if (whitelistedIPs.includes(request.ip)) {
146
+ // @ts-ignore
147
+ request.bypassRateLimit = true;
148
+ }
149
+ });
150
+
151
+ await app.register(rateLimit, {
152
+ redis,
153
+ skip: (request) => request.bypassRateLimit === true,
154
+ });
155
+ ```
156
+
157
+ ## Check Rate Limit Status
158
+
159
+ ```typescript
160
+ app.get('/me/rate-limit', {
161
+ preHandler: [app.authenticate],
162
+ handler: async (request, reply) => {
163
+ const key = `user:${request.user.id}`;
164
+ const current = await redis.get(`rate-limit:${key}`);
165
+ const ttl = await redis.ttl(`rate-limit:${key}`);
166
+
167
+ return {
168
+ limit: 1000,
169
+ used: parseInt(current || '0'),
170
+ remaining: 1000 - parseInt(current || '0'),
171
+ resetIn: ttl,
172
+ };
173
+ },
174
+ });
175
+ ```
176
+
177
+ ## Strategy Summary
178
+
179
+ | Endpoint | Max | Window | Key |
180
+ |----------|-----|--------|-----|
181
+ | Login | 5 | 15 min | IP |
182
+ | Register | 3 | 1 hour | IP + email |
183
+ | Public API | 100 | 15 min | IP |
184
+ | Authenticated | 1000 | 15 min | User ID |
185
+ | Premium | 5000 | 15 min | User ID |
186
+ | Admin | 10000 | 15 min | User ID |
187
+
188
+ ## Best Practices
189
+
190
+ ### ✅ DO:
191
+ - Use Redis for distributed rate limiting
192
+ - Set stricter limits for auth endpoints
193
+ - Return helpful error messages with retry time
194
+ - Log rate limit violations
195
+ - Add rate limit headers to responses
196
+ - Whitelist localhost for testing
197
+
198
+ ### ❌ DON'T:
199
+ - Use in-memory rate limiting in production
200
+ - Apply same limits to all endpoints
201
+ - Block users permanently without investigation
202
+ - Forget to test rate limits
203
+
204
+ ## Checklist
205
+
206
+ - [ ] Rate limiting configured with Redis
207
+ - [ ] Per-route limits set appropriately
208
+ - [ ] Custom error responses implemented
209
+ - [ ] Rate limit headers enabled
210
+ - [ ] Monitoring and logging in place
211
+ - [ ] Whitelist configured for testing