balda-js 0.0.1 → 0.0.2
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.
- package/lib/cli.d.ts +6 -0
- package/lib/cli.js +929 -0
- package/lib/cli.js.map +1 -0
- package/lib/index.cjs +3384 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.cts +1492 -0
- package/lib/index.d.ts +1492 -0
- package/lib/index.js +3327 -0
- package/lib/index.js.map +1 -0
- package/package.json +1 -1
- package/.husky/pre-commit +0 -19
- package/.nvmrc +0 -1
- package/docs/README.md +0 -135
- package/docs/blog/authors.yml +0 -6
- package/docs/blog/tags.yml +0 -4
- package/docs/cli.md +0 -109
- package/docs/docs/core-concepts/controllers.md +0 -393
- package/docs/docs/core-concepts/middleware.md +0 -302
- package/docs/docs/core-concepts/request-response.md +0 -486
- package/docs/docs/core-concepts/routing.md +0 -388
- package/docs/docs/core-concepts/server.md +0 -332
- package/docs/docs/cron/overview.md +0 -70
- package/docs/docs/examples/rest-api.md +0 -595
- package/docs/docs/getting-started/configuration.md +0 -168
- package/docs/docs/getting-started/installation.md +0 -125
- package/docs/docs/getting-started/quick-start.md +0 -273
- package/docs/docs/intro.md +0 -46
- package/docs/docs/plugins/cookie.md +0 -424
- package/docs/docs/plugins/cors.md +0 -295
- package/docs/docs/plugins/file.md +0 -382
- package/docs/docs/plugins/helmet.md +0 -388
- package/docs/docs/plugins/json.md +0 -338
- package/docs/docs/plugins/log.md +0 -592
- package/docs/docs/plugins/overview.md +0 -390
- package/docs/docs/plugins/rate-limiter.md +0 -347
- package/docs/docs/plugins/static.md +0 -352
- package/docs/docs/plugins/swagger.md +0 -411
- package/docs/docs/plugins/urlencoded.md +0 -76
- package/docs/docs/testing/examples.md +0 -384
- package/docs/docs/testing/mock-server.md +0 -311
- package/docs/docs/testing/overview.md +0 -76
- package/docs/docusaurus.config.ts +0 -144
- package/docs/intro.md +0 -78
- package/docs/package.json +0 -46
- package/docs/sidebars.ts +0 -72
- package/docs/static/.nojekyll +0 -0
- package/docs/static/img/docusaurus-social-card.jpg +0 -0
- package/docs/static/img/docusaurus.png +0 -0
- package/docs/static/img/favicon.ico +0 -0
- package/docs/static/img/logo.svg +0 -1
- package/docs/static/img/undraw_docusaurus_mountain.svg +0 -37
- package/docs/static/img/undraw_docusaurus_react.svg +0 -170
- package/docs/static/img/undraw_docusaurus_tree.svg +0 -40
- package/docs/tsconfig.json +0 -8
- package/speed_test.sh +0 -3
- package/test/benchmark/index.ts +0 -17
- package/test/cli/cli.ts +0 -7
- package/test/commands/test.ts +0 -42
- package/test/controllers/file_upload.ts +0 -29
- package/test/controllers/urlencoded.ts +0 -13
- package/test/controllers/users.ts +0 -111
- package/test/cron/index.ts +0 -6
- package/test/cron/test_cron.ts +0 -8
- package/test/cron/test_cron_imported.ts +0 -8
- package/test/native_env.ts +0 -16
- package/test/resources/test.txt +0 -1
- package/test/server/index.ts +0 -3
- package/test/server/instance.ts +0 -63
- package/test/suite/upload.test.ts +0 -23
- package/test/suite/urlencoded.test.ts +0 -23
- package/test/suite/users.test.ts +0 -76
- package/todo.md +0 -9
- package/tsconfig.json +0 -24
- package/vitest.config.ts +0 -17
@@ -1,390 +0,0 @@
|
|
1
|
-
---
|
2
|
-
sidebar_position: 1
|
3
|
-
---
|
4
|
-
|
5
|
-
# Plugins Overview
|
6
|
-
|
7
|
-
Balda.js provides a comprehensive plugin system that allows you to extend the framework's functionality with additional features and middleware.
|
8
|
-
Those are included by default in the framework as a standard library, you can use them directly or as a reference to create your own plugins.
|
9
|
-
|
10
|
-
## What are Plugins?
|
11
|
-
|
12
|
-
Plugins in Balda.js are modular components that add specific functionality to your application. They can provide:
|
13
|
-
|
14
|
-
- **Middleware**: Request/response processing
|
15
|
-
- **Body Parsing**: JSON, form data, file uploads
|
16
|
-
- **Security**: CORS, Helmet, rate limiting
|
17
|
-
- **Documentation**: Swagger/OpenAPI
|
18
|
-
- **Utilities**: Logging, cookies, static file serving
|
19
|
-
|
20
|
-
## Built-in Plugins
|
21
|
-
|
22
|
-
Balda.js comes with several built-in plugins:
|
23
|
-
|
24
|
-
### Core Plugins
|
25
|
-
|
26
|
-
- **[CORS](./cors.md)** - Cross-Origin Resource Sharing
|
27
|
-
- **[JSON](./json.md)** - JSON body parsing
|
28
|
-
- **[Static](./static.md)** - Static file serving
|
29
|
-
- **[Cookie](./cookie.md)** - Cookie parsing and management
|
30
|
-
|
31
|
-
### Security Plugins
|
32
|
-
|
33
|
-
- **[Helmet](./helmet.md)** - Security headers
|
34
|
-
- **[Rate Limiter](./rate-limiter.md)** - Request rate limiting
|
35
|
-
|
36
|
-
### Utility Plugins
|
37
|
-
|
38
|
-
- **[Log](./log.md)** - Request logging
|
39
|
-
- **[File](./file.md)** - File upload handling
|
40
|
-
- **[URL Encoded](./urlencoded.md)** - Form data parsing
|
41
|
-
|
42
|
-
### Documentation Plugins
|
43
|
-
|
44
|
-
- **[Swagger](./swagger.md)** - API documentation
|
45
|
-
|
46
|
-
## Plugin Configuration
|
47
|
-
|
48
|
-
### Basic Configuration
|
49
|
-
|
50
|
-
Plugins are configured through the server options:
|
51
|
-
|
52
|
-
```typescript
|
53
|
-
import { Server } from 'balda-js';
|
54
|
-
|
55
|
-
const server = new Server({
|
56
|
-
port: 3000,
|
57
|
-
plugins: {
|
58
|
-
cors: {
|
59
|
-
origin: ['http://localhost:3000'],
|
60
|
-
credentials: true
|
61
|
-
},
|
62
|
-
json: {
|
63
|
-
limit: '10mb'
|
64
|
-
},
|
65
|
-
static: {
|
66
|
-
root: './public'
|
67
|
-
}
|
68
|
-
}
|
69
|
-
});
|
70
|
-
```
|
71
|
-
|
72
|
-
### Environment-based Configuration
|
73
|
-
|
74
|
-
```typescript
|
75
|
-
const isProduction = process.env.NODE_ENV === 'production';
|
76
|
-
|
77
|
-
const server = new Server({
|
78
|
-
port: 3000,
|
79
|
-
plugins: {
|
80
|
-
cors: {
|
81
|
-
origin: isProduction
|
82
|
-
? ['https://myapp.com']
|
83
|
-
: ['http://localhost:3000']
|
84
|
-
},
|
85
|
-
helmet: isProduction ? {} : false,
|
86
|
-
rateLimiter: isProduction ? {
|
87
|
-
windowMs: 15 * 60 * 1000, // 15 minutes
|
88
|
-
max: 100 // limit each IP to 100 requests per windowMs
|
89
|
-
} : false
|
90
|
-
}
|
91
|
-
});
|
92
|
-
```
|
93
|
-
|
94
|
-
## Plugin Order
|
95
|
-
|
96
|
-
Plugins are applied in a specific order to ensure proper functionality:
|
97
|
-
|
98
|
-
1. **CORS** - Handle cross-origin requests first
|
99
|
-
2. **Helmet** - Apply security headers
|
100
|
-
3. **Rate Limiter** - Check request limits
|
101
|
-
4. **Body Parsers** - Parse request bodies
|
102
|
-
- JSON
|
103
|
-
- URL Encoded
|
104
|
-
- File uploads
|
105
|
-
5. **Cookie** - Parse cookies
|
106
|
-
6. **Log** - Log requests
|
107
|
-
7. **Static** - Serve static files
|
108
|
-
8. **Swagger** - API documentation
|
109
|
-
|
110
|
-
## Plugin Options
|
111
|
-
|
112
|
-
### Enabling/Disabling Plugins
|
113
|
-
|
114
|
-
```typescript
|
115
|
-
const server = new Server({
|
116
|
-
plugins: {
|
117
|
-
cors: true, // Use default configuration
|
118
|
-
json: false, // Disable plugin
|
119
|
-
static: { // Custom configuration
|
120
|
-
root: './public'
|
121
|
-
}
|
122
|
-
}
|
123
|
-
});
|
124
|
-
```
|
125
|
-
|
126
|
-
### Plugin-specific Options
|
127
|
-
|
128
|
-
Each plugin has its own configuration options:
|
129
|
-
|
130
|
-
```typescript
|
131
|
-
const server = new Server({
|
132
|
-
plugins: {
|
133
|
-
cors: {
|
134
|
-
origin: ['http://localhost:3000'],
|
135
|
-
methods: ['GET', 'POST', 'PUT', 'DELETE'],
|
136
|
-
credentials: true
|
137
|
-
},
|
138
|
-
json: {
|
139
|
-
limit: '10mb',
|
140
|
-
strict: true
|
141
|
-
},
|
142
|
-
static: {
|
143
|
-
root: './public',
|
144
|
-
prefix: '/static'
|
145
|
-
},
|
146
|
-
rateLimiter: {
|
147
|
-
windowMs: 15 * 60 * 1000,
|
148
|
-
max: 100
|
149
|
-
}
|
150
|
-
}
|
151
|
-
});
|
152
|
-
```
|
153
|
-
|
154
|
-
## Custom Plugins
|
155
|
-
|
156
|
-
### Creating a Custom Plugin
|
157
|
-
|
158
|
-
```typescript
|
159
|
-
import { ServerPlugin } from 'balda-js';
|
160
|
-
|
161
|
-
export class CustomPlugin implements ServerPlugin {
|
162
|
-
name = 'custom';
|
163
|
-
|
164
|
-
apply(server: Server) {
|
165
|
-
// Add middleware
|
166
|
-
server.use((req, res, next) => {
|
167
|
-
console.log('Custom plugin middleware');
|
168
|
-
next();
|
169
|
-
});
|
170
|
-
|
171
|
-
// Add routes
|
172
|
-
server.get('/custom', (req, res) => {
|
173
|
-
res.json({ message: 'Custom plugin route' });
|
174
|
-
});
|
175
|
-
}
|
176
|
-
}
|
177
|
-
|
178
|
-
// Usage
|
179
|
-
const server = new Server({
|
180
|
-
plugins: {
|
181
|
-
custom: new CustomPlugin()
|
182
|
-
}
|
183
|
-
});
|
184
|
-
```
|
185
|
-
|
186
|
-
### Plugin with Configuration
|
187
|
-
|
188
|
-
```typescript
|
189
|
-
interface CustomPluginOptions {
|
190
|
-
message?: string;
|
191
|
-
enabled?: boolean;
|
192
|
-
}
|
193
|
-
|
194
|
-
export class CustomPlugin implements ServerPlugin {
|
195
|
-
name = 'custom';
|
196
|
-
private options: CustomPluginOptions;
|
197
|
-
|
198
|
-
constructor(options: CustomPluginOptions = {}) {
|
199
|
-
this.options = {
|
200
|
-
message: 'Hello from custom plugin',
|
201
|
-
enabled: true,
|
202
|
-
...options
|
203
|
-
};
|
204
|
-
}
|
205
|
-
|
206
|
-
apply(server: Server) {
|
207
|
-
if (!this.options.enabled) {
|
208
|
-
return;
|
209
|
-
}
|
210
|
-
|
211
|
-
server.use((req, res, next) => {
|
212
|
-
console.log(this.options.message);
|
213
|
-
next();
|
214
|
-
});
|
215
|
-
}
|
216
|
-
}
|
217
|
-
|
218
|
-
// Usage
|
219
|
-
const server = new Server({
|
220
|
-
plugins: {
|
221
|
-
custom: {
|
222
|
-
message: 'Custom message',
|
223
|
-
enabled: true
|
224
|
-
}
|
225
|
-
}
|
226
|
-
});
|
227
|
-
```
|
228
|
-
|
229
|
-
## Plugin Lifecycle
|
230
|
-
|
231
|
-
### Initialization
|
232
|
-
|
233
|
-
1. Server creates plugin instances
|
234
|
-
2. Plugins are validated
|
235
|
-
3. Plugins are applied in order
|
236
|
-
4. Middleware is registered
|
237
|
-
|
238
|
-
### Request Processing
|
239
|
-
|
240
|
-
1. Request enters plugin middleware chain
|
241
|
-
2. Each plugin processes the request
|
242
|
-
3. Request reaches route handlers
|
243
|
-
4. Response flows back through plugins
|
244
|
-
|
245
|
-
## Best Practices
|
246
|
-
|
247
|
-
### 1. Configure Based on Environment
|
248
|
-
|
249
|
-
```typescript
|
250
|
-
const isProduction = process.env.NODE_ENV === 'production';
|
251
|
-
|
252
|
-
const server = new Server({
|
253
|
-
plugins: {
|
254
|
-
cors: {
|
255
|
-
origin: isProduction
|
256
|
-
? process.env.ALLOWED_ORIGINS?.split(',')
|
257
|
-
: ['http://localhost:3000'],
|
258
|
-
credentials: true
|
259
|
-
},
|
260
|
-
helmet: isProduction ? {} : false,
|
261
|
-
rateLimiter: isProduction ? {
|
262
|
-
windowMs: 15 * 60 * 1000,
|
263
|
-
max: 100
|
264
|
-
} : false
|
265
|
-
}
|
266
|
-
});
|
267
|
-
```
|
268
|
-
|
269
|
-
### 2. Use Appropriate Limits
|
270
|
-
|
271
|
-
```typescript
|
272
|
-
const server = new Server({
|
273
|
-
plugins: {
|
274
|
-
json: {
|
275
|
-
limit: '10mb' // Reasonable limit for JSON payloads
|
276
|
-
},
|
277
|
-
file: {
|
278
|
-
maxFileSize: '5mb', // Limit file uploads
|
279
|
-
maxFiles: 10
|
280
|
-
}
|
281
|
-
}
|
282
|
-
});
|
283
|
-
```
|
284
|
-
|
285
|
-
### 3. Secure Your Application
|
286
|
-
|
287
|
-
```typescript
|
288
|
-
const server = new Server({
|
289
|
-
plugins: {
|
290
|
-
helmet: {
|
291
|
-
contentSecurityPolicy: {
|
292
|
-
directives: {
|
293
|
-
defaultSrc: ["'self'"],
|
294
|
-
styleSrc: ["'self'", "'unsafe-inline'"],
|
295
|
-
scriptSrc: ["'self'"]
|
296
|
-
}
|
297
|
-
}
|
298
|
-
},
|
299
|
-
cors: {
|
300
|
-
origin: ['https://myapp.com'],
|
301
|
-
credentials: true
|
302
|
-
}
|
303
|
-
}
|
304
|
-
});
|
305
|
-
```
|
306
|
-
|
307
|
-
### 4. Monitor and Log
|
308
|
-
|
309
|
-
```typescript
|
310
|
-
const server = new Server({
|
311
|
-
plugins: {
|
312
|
-
log: {
|
313
|
-
level: 'info',
|
314
|
-
format: 'combined'
|
315
|
-
},
|
316
|
-
rateLimiter: {
|
317
|
-
windowMs: 15 * 60 * 1000,
|
318
|
-
max: 100,
|
319
|
-
message: 'Too many requests from this IP'
|
320
|
-
}
|
321
|
-
}
|
322
|
-
});
|
323
|
-
```
|
324
|
-
|
325
|
-
### 5. Optimize for Performance
|
326
|
-
|
327
|
-
```typescript
|
328
|
-
const server = new Server({
|
329
|
-
plugins: {
|
330
|
-
static: {
|
331
|
-
root: './public',
|
332
|
-
maxAge: '1d', // Cache static files
|
333
|
-
etag: true
|
334
|
-
},
|
335
|
-
json: {
|
336
|
-
limit: '1mb' // Reasonable limit
|
337
|
-
}
|
338
|
-
}
|
339
|
-
});
|
340
|
-
```
|
341
|
-
|
342
|
-
## Plugin Compatibility
|
343
|
-
|
344
|
-
### Runtime Support
|
345
|
-
|
346
|
-
All built-in plugins work across all supported runtimes:
|
347
|
-
|
348
|
-
- **Node.js**: Full support
|
349
|
-
- **Bun**: Full support
|
350
|
-
- **Deno**: Full support
|
351
|
-
|
352
|
-
### Plugin Dependencies
|
353
|
-
|
354
|
-
Some plugins may have runtime-specific dependencies:
|
355
|
-
|
356
|
-
```typescript
|
357
|
-
// Plugin automatically detects runtime and uses appropriate implementation
|
358
|
-
const server = new Server({
|
359
|
-
plugins: {
|
360
|
-
file: {
|
361
|
-
// Works on Node.js, Bun, and Deno
|
362
|
-
storage: 'memory' // or 'disk'
|
363
|
-
}
|
364
|
-
}
|
365
|
-
});
|
366
|
-
```
|
367
|
-
|
368
|
-
## Troubleshooting
|
369
|
-
|
370
|
-
### Common Issues
|
371
|
-
|
372
|
-
1. **Plugin not working**: Check plugin order and configuration
|
373
|
-
2. **CORS errors**: Verify origin configuration
|
374
|
-
3. **File upload failures**: Check file size limits and storage configuration
|
375
|
-
4. **Rate limiting too strict**: Adjust window and max request settings
|
376
|
-
|
377
|
-
### Debugging
|
378
|
-
|
379
|
-
Enable debug logging to troubleshoot plugin issues:
|
380
|
-
|
381
|
-
```typescript
|
382
|
-
const server = new Server({
|
383
|
-
debug: true, // Enable debug logging
|
384
|
-
plugins: {
|
385
|
-
// Your plugin configuration
|
386
|
-
}
|
387
|
-
});
|
388
|
-
```
|
389
|
-
|
390
|
-
Plugins in Balda.js provide a powerful and flexible way to extend your application with additional functionality while maintaining clean, modular code.
|
@@ -1,347 +0,0 @@
|
|
1
|
-
---
|
2
|
-
sidebar_position: 7
|
3
|
-
---
|
4
|
-
|
5
|
-
# Rate Limiter Plugin
|
6
|
-
|
7
|
-
The Rate Limiter plugin helps protect your Balda.js application from abuse by limiting the number of requests a client can make within a specified time window. It supports both IP-based and custom key-based rate limiting with flexible storage options.
|
8
|
-
|
9
|
-
## Features
|
10
|
-
|
11
|
-
- **IP-Based Limiting**: Limit requests per IP address
|
12
|
-
- **Custom Key Limiting**: Limit requests based on custom criteria (user ID, API key, etc.)
|
13
|
-
- **Flexible Storage**: In-memory storage with custom storage support
|
14
|
-
- **Configurable Limits**: Set custom request limits and time windows
|
15
|
-
- **Customizable Responses**: Configure error messages and status codes
|
16
|
-
|
17
|
-
## Basic Configuration
|
18
|
-
|
19
|
-
### Simple IP-Based Limiting
|
20
|
-
|
21
|
-
```typescript
|
22
|
-
import { Server } from 'balda-js';
|
23
|
-
|
24
|
-
const server = new Server({
|
25
|
-
port: 3000,
|
26
|
-
plugins: {
|
27
|
-
rateLimiter: {
|
28
|
-
type: "ip",
|
29
|
-
limit: 100,
|
30
|
-
windowMs: 60000 // 1 minute
|
31
|
-
}
|
32
|
-
}
|
33
|
-
});
|
34
|
-
```
|
35
|
-
|
36
|
-
### Custom Key-Based Limiting
|
37
|
-
|
38
|
-
```typescript
|
39
|
-
const server = new Server({
|
40
|
-
port: 3000,
|
41
|
-
plugins: {
|
42
|
-
rateLimiter: {
|
43
|
-
type: "custom",
|
44
|
-
key: (req) => req.headers.get('X-API-Key') || req.ip,
|
45
|
-
limit: 50,
|
46
|
-
windowMs: 60000
|
47
|
-
}
|
48
|
-
}
|
49
|
-
});
|
50
|
-
```
|
51
|
-
|
52
|
-
## Configuration Options
|
53
|
-
|
54
|
-
### IP-Based Rate Limiting
|
55
|
-
|
56
|
-
```typescript
|
57
|
-
rateLimiter: {
|
58
|
-
type: "ip",
|
59
|
-
limit: 100, // Requests per window
|
60
|
-
windowMs: 60000, // Time window in milliseconds
|
61
|
-
message: "Too many requests", // Custom error message
|
62
|
-
statusCode: 429 // Custom status code
|
63
|
-
}
|
64
|
-
```
|
65
|
-
|
66
|
-
### Custom Key-Based Rate Limiting
|
67
|
-
|
68
|
-
```typescript
|
69
|
-
rateLimiter: {
|
70
|
-
type: "custom",
|
71
|
-
key: (req) => {
|
72
|
-
// Custom key generation logic
|
73
|
-
return req.headers.get('X-API-Key') || req.ip;
|
74
|
-
},
|
75
|
-
limit: 50,
|
76
|
-
windowMs: 60000,
|
77
|
-
message: "Rate limit exceeded",
|
78
|
-
statusCode: 429
|
79
|
-
}
|
80
|
-
```
|
81
|
-
|
82
|
-
### Storage Configuration
|
83
|
-
|
84
|
-
```typescript
|
85
|
-
rateLimiter: {
|
86
|
-
type: "ip",
|
87
|
-
limit: 100,
|
88
|
-
windowMs: 60000,
|
89
|
-
storageStrategy: "memory", // Default: "memory"
|
90
|
-
// For custom storage:
|
91
|
-
storageStrategy: "custom",
|
92
|
-
get: async (key) => { /* custom get logic */ },
|
93
|
-
set: async (key, value) => { /* custom set logic */ }
|
94
|
-
}
|
95
|
-
```
|
96
|
-
|
97
|
-
## Usage Examples
|
98
|
-
|
99
|
-
### Basic Rate Limiting
|
100
|
-
|
101
|
-
```typescript
|
102
|
-
const server = new Server({
|
103
|
-
port: 3000,
|
104
|
-
plugins: {
|
105
|
-
rateLimiter: {
|
106
|
-
type: "ip",
|
107
|
-
limit: 100,
|
108
|
-
windowMs: 60000 // 1 minute
|
109
|
-
}
|
110
|
-
}
|
111
|
-
});
|
112
|
-
|
113
|
-
@controller('/api')
|
114
|
-
export class ApiController {
|
115
|
-
@get('/users')
|
116
|
-
async getUsers(req: Request, res: Response) {
|
117
|
-
// This endpoint is rate limited to 100 requests per minute per IP
|
118
|
-
const users = await getUsers();
|
119
|
-
res.json(users);
|
120
|
-
}
|
121
|
-
}
|
122
|
-
```
|
123
|
-
|
124
|
-
### Different Limits for Different Endpoints
|
125
|
-
|
126
|
-
```typescript
|
127
|
-
@controller('/api')
|
128
|
-
export class ApiController {
|
129
|
-
@get('/public', {
|
130
|
-
middleware: [rateLimiter({
|
131
|
-
type: "ip",
|
132
|
-
limit: 1000,
|
133
|
-
windowMs: 60000
|
134
|
-
})]
|
135
|
-
})
|
136
|
-
async publicEndpoint(req: Request, res: Response) {
|
137
|
-
// 1000 requests per minute
|
138
|
-
res.json({ message: 'Public endpoint' });
|
139
|
-
}
|
140
|
-
|
141
|
-
@get('/sensitive', {
|
142
|
-
middleware: [rateLimiter({
|
143
|
-
type: "ip",
|
144
|
-
limit: 10,
|
145
|
-
windowMs: 60000
|
146
|
-
})]
|
147
|
-
})
|
148
|
-
async sensitiveEndpoint(req: Request, res: Response) {
|
149
|
-
// 10 requests per minute
|
150
|
-
res.json({ message: 'Sensitive endpoint' });
|
151
|
-
}
|
152
|
-
}
|
153
|
-
```
|
154
|
-
|
155
|
-
### User-Based Rate Limiting
|
156
|
-
|
157
|
-
```typescript
|
158
|
-
@controller('/api')
|
159
|
-
export class UserController {
|
160
|
-
@get('/profile', {
|
161
|
-
middleware: [rateLimiter({
|
162
|
-
type: "custom",
|
163
|
-
key: (req) => {
|
164
|
-
// Rate limit by user ID from JWT token
|
165
|
-
const token = req.headers.get('Authorization')?.replace('Bearer ', '');
|
166
|
-
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
167
|
-
return decoded.userId;
|
168
|
-
},
|
169
|
-
limit: 50,
|
170
|
-
windowMs: 60000
|
171
|
-
})]
|
172
|
-
})
|
173
|
-
async getProfile(req: Request, res: Response) {
|
174
|
-
// 50 requests per minute per user
|
175
|
-
const profile = await getUserProfile(req.user.id);
|
176
|
-
res.json(profile);
|
177
|
-
}
|
178
|
-
}
|
179
|
-
```
|
180
|
-
|
181
|
-
### API Key-Based Rate Limiting
|
182
|
-
|
183
|
-
```typescript
|
184
|
-
@controller('/api')
|
185
|
-
export class ApiKeyController {
|
186
|
-
@get('/data', {
|
187
|
-
middleware: [rateLimiter({
|
188
|
-
type: "custom",
|
189
|
-
key: (req) => req.headers.get('X-API-Key'),
|
190
|
-
limit: 1000,
|
191
|
-
windowMs: 60000
|
192
|
-
})]
|
193
|
-
})
|
194
|
-
async getData(req: Request, res: Response) {
|
195
|
-
// 1000 requests per minute per API key
|
196
|
-
const data = await getData();
|
197
|
-
res.json(data);
|
198
|
-
}
|
199
|
-
}
|
200
|
-
```
|
201
|
-
|
202
|
-
## Advanced Configuration
|
203
|
-
|
204
|
-
### Multiple Rate Limiters
|
205
|
-
|
206
|
-
```typescript
|
207
|
-
@controller('/api')
|
208
|
-
export class AdvancedController {
|
209
|
-
@get('/complex', {
|
210
|
-
middleware: [
|
211
|
-
// IP-based rate limiting
|
212
|
-
rateLimiter({
|
213
|
-
type: "ip",
|
214
|
-
limit: 100,
|
215
|
-
windowMs: 60000
|
216
|
-
}),
|
217
|
-
// User-based rate limiting
|
218
|
-
rateLimiter({
|
219
|
-
type: "custom",
|
220
|
-
key: (req) => req.user?.id || req.ip,
|
221
|
-
limit: 20,
|
222
|
-
windowMs: 60000
|
223
|
-
})
|
224
|
-
]
|
225
|
-
})
|
226
|
-
async complexEndpoint(req: Request, res: Response) {
|
227
|
-
// Must pass both rate limiters
|
228
|
-
res.json({ message: 'Complex endpoint' });
|
229
|
-
}
|
230
|
-
}
|
231
|
-
```
|
232
|
-
|
233
|
-
### Custom Storage Implementation
|
234
|
-
|
235
|
-
```typescript
|
236
|
-
// Redis storage example
|
237
|
-
const redisStorage = {
|
238
|
-
get: async (key: string) => {
|
239
|
-
const value = await redis.get(`rate_limit:${key}`);
|
240
|
-
return value ? parseInt(value) : 0;
|
241
|
-
},
|
242
|
-
set: async (key: string, value: number) => {
|
243
|
-
await redis.setex(`rate_limit:${key}`, 60, value.toString());
|
244
|
-
}
|
245
|
-
};
|
246
|
-
|
247
|
-
const server = new Server({
|
248
|
-
port: 3000,
|
249
|
-
plugins: {
|
250
|
-
rateLimiter: {
|
251
|
-
type: "ip",
|
252
|
-
limit: 100,
|
253
|
-
windowMs: 60000,
|
254
|
-
storageStrategy: "custom",
|
255
|
-
get: redisStorage.get,
|
256
|
-
set: redisStorage.set
|
257
|
-
}
|
258
|
-
}
|
259
|
-
});
|
260
|
-
```
|
261
|
-
|
262
|
-
### Environment-Based Configuration
|
263
|
-
|
264
|
-
```typescript
|
265
|
-
const isProduction = process.env.NODE_ENV === 'production';
|
266
|
-
|
267
|
-
const server = new Server({
|
268
|
-
port: 3000,
|
269
|
-
plugins: {
|
270
|
-
rateLimiter: {
|
271
|
-
type: "ip",
|
272
|
-
limit: isProduction ? 100 : 1000, // Stricter in production
|
273
|
-
windowMs: 60000,
|
274
|
-
message: isProduction ? "Rate limit exceeded" : "Too many requests",
|
275
|
-
statusCode: 429
|
276
|
-
}
|
277
|
-
}
|
278
|
-
});
|
279
|
-
```
|
280
|
-
|
281
|
-
## Error Handling
|
282
|
-
|
283
|
-
### Rate Limit Exceeded Response
|
284
|
-
|
285
|
-
```typescript
|
286
|
-
// Default response when rate limit is exceeded
|
287
|
-
{
|
288
|
-
"message": "ERR_RATE_LIMIT_EXCEEDED"
|
289
|
-
}
|
290
|
-
|
291
|
-
// Custom response
|
292
|
-
{
|
293
|
-
"message": "Too many requests, please try again later"
|
294
|
-
}
|
295
|
-
```
|
296
|
-
|
297
|
-
### Custom Error Handling
|
298
|
-
|
299
|
-
```typescript
|
300
|
-
@controller('/api')
|
301
|
-
export class ErrorController {
|
302
|
-
@get('/limited', {
|
303
|
-
middleware: [rateLimiter({
|
304
|
-
type: "ip",
|
305
|
-
limit: 5,
|
306
|
-
windowMs: 60000,
|
307
|
-
message: "You've exceeded the rate limit",
|
308
|
-
statusCode: 429
|
309
|
-
})]
|
310
|
-
})
|
311
|
-
async limitedEndpoint(req: Request, res: Response) {
|
312
|
-
res.json({ message: 'Success' });
|
313
|
-
}
|
314
|
-
}
|
315
|
-
```
|
316
|
-
|
317
|
-
## Storage Strategies
|
318
|
-
|
319
|
-
### In-Memory Storage (Default)
|
320
|
-
|
321
|
-
```typescript
|
322
|
-
rateLimiter: {
|
323
|
-
type: "ip",
|
324
|
-
limit: 100,
|
325
|
-
windowMs: 60000,
|
326
|
-
storageStrategy: "memory" // Default
|
327
|
-
}
|
328
|
-
```
|
329
|
-
|
330
|
-
### Custom Storage
|
331
|
-
|
332
|
-
```typescript
|
333
|
-
rateLimiter: {
|
334
|
-
type: "ip",
|
335
|
-
limit: 100,
|
336
|
-
windowMs: 60000,
|
337
|
-
storageStrategy: "custom",
|
338
|
-
get: async (key: string) => {
|
339
|
-
// Custom get implementation
|
340
|
-
return await yourStorage.get(key);
|
341
|
-
},
|
342
|
-
set: async (key: string, value: number) => {
|
343
|
-
// Custom set implementation
|
344
|
-
await yourStorage.set(key, value, 60); // 60 seconds TTL
|
345
|
-
}
|
346
|
-
}
|
347
|
-
```
|