0http-bun 1.1.3 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,870 @@
1
+ # Middleware Documentation
2
+
3
+ 0http-bun provides a comprehensive middleware system with built-in middlewares for common use cases. All middleware functions are TypeScript-ready and follow the standard middleware pattern.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Middleware Pattern](#middleware-pattern)
8
+ - [Built-in Middlewares](#built-in-middlewares)
9
+ - [Body Parser](#body-parser)
10
+ - [CORS](#cors)
11
+ - [JWT Authentication](#jwt-authentication)
12
+ - [Logger](#logger)
13
+ - [Rate Limiting](#rate-limiting)
14
+ - [Creating Custom Middleware](#creating-custom-middleware)
15
+
16
+ ## Middleware Pattern
17
+
18
+ All middlewares in 0http-bun follow the standard pattern:
19
+
20
+ ```typescript
21
+ import {ZeroRequest, StepFunction} from '0http-bun'
22
+
23
+ type Middleware = (
24
+ req: ZeroRequest,
25
+ next: StepFunction,
26
+ ) => Promise<Response> | Response
27
+ ```
28
+
29
+ ### TypeScript Support
30
+
31
+ TypeScript type definitions are available for both the core framework and middleware modules:
32
+
33
+ ```typescript
34
+ // Core framework types (from root module)
35
+ import {ZeroRequest, StepFunction, RequestHandler} from '0http-bun'
36
+
37
+ // Middleware-specific types (from middleware module)
38
+ import type {
39
+ LoggerOptions,
40
+ JWTAuthOptions,
41
+ RateLimitOptions,
42
+ RateLimitStore,
43
+ MemoryStore,
44
+ CORSOptions,
45
+ BodyParserOptions,
46
+ } from '0http-bun/lib/middleware'
47
+
48
+ // Import middleware functions
49
+ import {
50
+ createLogger,
51
+ createJWTAuth,
52
+ createRateLimit,
53
+ } from '0http-bun/lib/middleware'
54
+ ```
55
+
56
+ ## Built-in Middlewares
57
+
58
+ All middleware can be imported from the main middleware module:
59
+
60
+ ```javascript
61
+ // Import all middleware from the middleware index
62
+ const {
63
+ createBodyParser,
64
+ createCORS,
65
+ createJWTAuth,
66
+ createLogger,
67
+ createRateLimit,
68
+ } = require('0http-bun/lib/middleware')
69
+ ```
70
+
71
+ For TypeScript:
72
+
73
+ ```typescript
74
+ // Import middleware functions
75
+ import {
76
+ createBodyParser,
77
+ createCORS,
78
+ createJWTAuth,
79
+ createLogger,
80
+ createRateLimit,
81
+ } from '0http-bun/lib/middleware'
82
+
83
+ // Import types
84
+ import type {
85
+ BodyParserOptions,
86
+ CORSOptions,
87
+ JWTAuthOptions,
88
+ LoggerOptions,
89
+ RateLimitOptions,
90
+ } from '0http-bun/lib/middleware'
91
+ ```
92
+
93
+ ### Body Parser
94
+
95
+ Automatically parses request bodies based on Content-Type header.
96
+
97
+ ```javascript
98
+ const {createBodyParser} = require('0http-bun/lib/middleware')
99
+
100
+ const router = http()
101
+
102
+ // Basic usage
103
+ router.use(createBodyParser())
104
+
105
+ // Access parsed body
106
+ router.post('/api/data', (req) => {
107
+ console.log(req.body) // Parsed body content
108
+ return Response.json({received: req.body})
109
+ })
110
+ ```
111
+
112
+ **TypeScript Usage:**
113
+
114
+ ```typescript
115
+ import {createBodyParser} from '0http-bun/lib/middleware'
116
+ import type {BodyParserOptions} from '0http-bun/lib/middleware'
117
+
118
+ // With custom configuration
119
+ const bodyParserOptions: BodyParserOptions = {
120
+ json: {
121
+ limit: 10 * 1024 * 1024, // 10MB
122
+ strict: true,
123
+ },
124
+ urlencoded: {
125
+ extended: true,
126
+ limit: 1024 * 1024, // 1MB
127
+ },
128
+ }
129
+
130
+ router.use(createBodyParser(bodyParserOptions))
131
+ ```
132
+
133
+ **Supported Content Types:**
134
+
135
+ - `application/json` - Parsed as JSON
136
+ - `application/x-www-form-urlencoded` - Parsed as form data
137
+ - `multipart/form-data` - Parsed as FormData
138
+ - `text/*` - Parsed as plain text
139
+ - `application/octet-stream` - Parsed as ArrayBuffer
140
+
141
+ ### CORS
142
+
143
+ Cross-Origin Resource Sharing middleware with flexible configuration.
144
+
145
+ ```javascript
146
+ const {createCORS} = require('0http-bun/lib/middleware')
147
+
148
+ // Basic usage (allows all origins)
149
+ router.use(createCORS())
150
+
151
+ // Custom configuration
152
+ router.use(
153
+ createCORS({
154
+ origin: ['https://example.com', 'https://app.example.com'],
155
+ methods: ['GET', 'POST', 'PUT', 'DELETE'],
156
+ allowedHeaders: ['Content-Type', 'Authorization'],
157
+ exposedHeaders: ['X-Total-Count'],
158
+ credentials: true,
159
+ maxAge: 86400, // Preflight cache duration (seconds)
160
+ preflightContinue: false,
161
+ optionsSuccessStatus: 204,
162
+ }),
163
+ )
164
+
165
+ // Dynamic origin validation
166
+ router.use(
167
+ createCORS({
168
+ origin: (origin, req) => {
169
+ // Custom logic to validate origin
170
+ return (
171
+ origin?.endsWith('.mycompany.com') || origin === 'http://localhost:3000'
172
+ )
173
+ },
174
+ }),
175
+ )
176
+ ```
177
+
178
+ **TypeScript Usage:**
179
+
180
+ ```typescript
181
+ import {createCORS} from '0http-bun/lib/middleware'
182
+ import type {CORSOptions} from '0http-bun/lib/middleware'
183
+
184
+ const corsOptions: CORSOptions = {
185
+ origin: ['https://example.com', 'https://app.example.com'],
186
+ methods: ['GET', 'POST', 'PUT', 'DELETE'],
187
+ credentials: true,
188
+ }
189
+
190
+ router.use(createCORS(corsOptions))
191
+ ```
192
+
193
+ ### JWT Authentication
194
+
195
+ JSON Web Token authentication and authorization middleware with support for static secrets, JWKS endpoints, and API key authentication.
196
+
197
+ #### Basic JWT with Static Secret
198
+
199
+ ```javascript
200
+ const {createJWTAuth} = require('0http-bun/lib/middleware')
201
+
202
+ // Basic JWT verification with static secret
203
+ router.use(
204
+ '/api/protected/*',
205
+ createJWTAuth({
206
+ secret: 'your-secret-key',
207
+ algorithms: ['HS256'],
208
+ }),
209
+ )
210
+ ```
211
+
212
+ **TypeScript Usage:**
213
+
214
+ ```typescript
215
+ import {createJWTAuth} from '0http-bun/lib/middleware'
216
+ import type {JWTAuthOptions} from '0http-bun/lib/middleware'
217
+
218
+ const jwtOptions: JWTAuthOptions = {
219
+ secret: 'your-secret-key',
220
+ jwtOptions: {
221
+ algorithms: ['HS256'],
222
+ audience: 'your-api',
223
+ issuer: 'your-service',
224
+ },
225
+ }
226
+
227
+ router.use('/api/protected/*', createJWTAuth(jwtOptions))
228
+ ```
229
+
230
+ #### JWT with JWKS URI (Recommended for Production)
231
+
232
+ For production applications, especially when integrating with identity providers like Auth0, AWS Cognito, or Azure AD, use JWKS URI for automatic key rotation:
233
+
234
+ ```typescript
235
+ // Using JWKS URI (Auth0 example)
236
+ router.use(
237
+ '/api/protected/*',
238
+ createJWTAuth({
239
+ jwksUri: 'https://your-domain.auth0.com/.well-known/jwks.json',
240
+ algorithms: ['RS256'],
241
+ issuer: 'https://your-domain.auth0.com/',
242
+ audience: 'your-api-identifier',
243
+ }),
244
+ )
245
+
246
+ // AWS Cognito example
247
+ router.use(
248
+ '/api/protected/*',
249
+ createJWTAuth({
250
+ jwksUri:
251
+ 'https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json',
252
+ algorithms: ['RS256'],
253
+ issuer: 'https://cognito-idp.{region}.amazonaws.com/{userPoolId}',
254
+ audience: 'your-client-id',
255
+ }),
256
+ )
257
+
258
+ // Azure AD example
259
+ router.use(
260
+ '/api/protected/*',
261
+ createJWTAuth({
262
+ jwksUri: 'https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys',
263
+ algorithms: ['RS256'],
264
+ issuer: 'https://login.microsoftonline.com/{tenant}/v2.0',
265
+ audience: 'your-application-id',
266
+ }),
267
+ )
268
+
269
+ // Google Identity example
270
+ router.use(
271
+ '/api/protected/*',
272
+ createJWTAuth({
273
+ jwksUri: 'https://www.googleapis.com/oauth2/v3/certs',
274
+ algorithms: ['RS256'],
275
+ issuer: 'https://accounts.google.com',
276
+ audience: 'your-client-id.apps.googleusercontent.com',
277
+ }),
278
+ )
279
+ ```
280
+
281
+ #### Advanced Configuration
282
+
283
+ ```typescript
284
+ // Complete configuration example
285
+ router.use(
286
+ createJWTAuth({
287
+ // Option 1: Static secret (for development/simple cases)
288
+ secret: process.env.JWT_SECRET,
289
+
290
+ // Option 2: JWKS URI (recommended for production)
291
+ jwksUri: process.env.JWKS_URI,
292
+
293
+ // JWT verification options
294
+ algorithms: ['HS256', 'RS256'],
295
+ issuer: 'your-app',
296
+ audience: 'your-users',
297
+ clockTolerance: 10, // Clock skew tolerance (seconds)
298
+ ignoreExpiration: false,
299
+ ignoreNotBefore: false,
300
+
301
+ // Custom token extraction
302
+ getToken: (req) => {
303
+ // Try multiple sources
304
+ return (
305
+ req.headers.get('x-auth-token') ||
306
+ req.headers.get('authorization')?.replace('Bearer ', '') ||
307
+ new URL(req.url).searchParams.get('token')
308
+ )
309
+ },
310
+
311
+ // Alternative token sources
312
+ tokenHeader: 'x-custom-token', // Custom header name
313
+ tokenQuery: 'access_token', // Query parameter name
314
+
315
+ // Error handling
316
+ onError: (err, req) => {
317
+ console.error('JWT Error:', err)
318
+ return Response.json(
319
+ {
320
+ error: 'Unauthorized',
321
+ code: err.name,
322
+ message:
323
+ process.env.NODE_ENV === 'development' ? err.message : undefined,
324
+ },
325
+ {status: 401},
326
+ )
327
+ },
328
+
329
+ // Custom unauthorized response
330
+ unauthorizedResponse: (error, req) => {
331
+ return Response.json(
332
+ {
333
+ error: 'Access denied',
334
+ requestId: req.headers.get('x-request-id'),
335
+ timestamp: new Date().toISOString(),
336
+ },
337
+ {status: 401},
338
+ )
339
+ },
340
+
341
+ // Optional authentication (proceed even without token)
342
+ optional: false,
343
+
344
+ // Exclude certain paths
345
+ excludePaths: ['/health', '/metrics', '/api/public'],
346
+ }),
347
+ )
348
+ ```
349
+
350
+ #### API Key Authentication
351
+
352
+ The JWT middleware also supports API key authentication as an alternative or fallback:
353
+
354
+ ```typescript
355
+ // API key with static keys
356
+ router.use(
357
+ '/api/*',
358
+ createJWTAuth({
359
+ apiKeys: ['key1', 'key2', 'key3'],
360
+ apiKeyHeader: 'x-api-key', // Default header
361
+ }),
362
+ )
363
+
364
+ // API key with custom validation
365
+ router.use(
366
+ '/api/*',
367
+ createJWTAuth({
368
+ apiKeyValidator: async (apiKey, req) => {
369
+ // Custom validation logic
370
+ const user = await validateApiKeyInDatabase(apiKey)
371
+ return user ? {id: user.id, name: user.name, apiKey} : false
372
+ },
373
+ apiKeyHeader: 'x-api-key',
374
+ }),
375
+ )
376
+
377
+ // Combined JWT + API Key authentication
378
+ router.use(
379
+ '/api/*',
380
+ createJWTAuth({
381
+ // JWT configuration
382
+ jwksUri: process.env.JWKS_URI,
383
+ algorithms: ['RS256'],
384
+
385
+ // API Key fallback
386
+ apiKeys: process.env.API_KEYS?.split(','),
387
+ apiKeyHeader: 'x-api-key',
388
+
389
+ // If JWT fails, try API key
390
+ optional: false,
391
+ }),
392
+ )
393
+ ```
394
+
395
+ #### Environment-Based Configuration
396
+
397
+ ```typescript
398
+ // Dynamic configuration based on environment
399
+ const jwtConfig =
400
+ process.env.NODE_ENV === 'production'
401
+ ? {
402
+ // Production: Use JWKS for security and key rotation
403
+ jwksUri: process.env.JWKS_URI,
404
+ algorithms: ['RS256'],
405
+ issuer: process.env.JWT_ISSUER,
406
+ audience: process.env.JWT_AUDIENCE,
407
+ }
408
+ : {
409
+ // Development: Use static secret for simplicity
410
+ secret: process.env.JWT_SECRET || 'dev-secret-key',
411
+ algorithms: ['HS256'],
412
+ }
413
+
414
+ router.use('/api/protected/*', createJWTAuth(jwtConfig))
415
+ ```
416
+
417
+ #### Access Decoded Token Data
418
+
419
+ ```typescript
420
+ // Access decoded token in route handlers
421
+ router.get('/api/profile', (req) => {
422
+ // Multiple ways to access user data
423
+ console.log(req.user) // Decoded JWT payload
424
+ console.log(req.ctx.user) // Same as req.user
425
+ console.log(req.jwt) // Full JWT info (payload, header, token)
426
+ console.log(req.ctx.jwt) // Same as req.jwt
427
+
428
+ // API key authentication data (if used)
429
+ console.log(req.apiKey) // API key value
430
+ console.log(req.ctx.apiKey) // Same as req.apiKey
431
+
432
+ return Response.json({
433
+ user: req.user,
434
+ tokenInfo: {
435
+ issuer: req.jwt?.payload.iss,
436
+ audience: req.jwt?.payload.aud,
437
+ expiresAt: new Date(req.jwt?.payload.exp * 1000),
438
+ issuedAt: new Date(req.jwt?.payload.iat * 1000),
439
+ },
440
+ })
441
+ })
442
+ ```
443
+
444
+ ### Logger
445
+
446
+ Request logging middleware with customizable output formats.
447
+
448
+ ```javascript
449
+ const {createLogger, simpleLogger} = require('0http-bun/lib/middleware')
450
+
451
+ // Simple logging
452
+ router.use(simpleLogger())
453
+
454
+ // Detailed logging with custom format
455
+ router.use(
456
+ createLogger({
457
+ pinoOptions: {
458
+ level: 'info',
459
+ transport: {
460
+ target: 'pino-pretty',
461
+ options: {colorize: true},
462
+ },
463
+ },
464
+ logBody: false,
465
+ excludePaths: ['/health', '/metrics'],
466
+ }),
467
+ )
468
+ ```
469
+
470
+ **TypeScript Usage:**
471
+
472
+ ```typescript
473
+ import {createLogger, simpleLogger} from '0http-bun/lib/middleware'
474
+ import type {LoggerOptions} from '0http-bun/lib/middleware'
475
+
476
+ const loggerOptions: LoggerOptions = {
477
+ pinoOptions: {
478
+ level: 'info',
479
+ transport: {
480
+ target: 'pino-pretty',
481
+ options: {colorize: true},
482
+ },
483
+ },
484
+ logBody: true,
485
+ excludePaths: ['/health', '/ping'],
486
+ serializers: {
487
+ req: (req) => ({
488
+ method: req.method,
489
+ url: req.url,
490
+ userAgent: req.headers.get('user-agent'),
491
+ }),
492
+ },
493
+ }
494
+
495
+ router.use(createLogger(loggerOptions))
496
+ ```
497
+
498
+ **Available Formats:**
499
+
500
+ - `combined` - Apache Combined Log Format
501
+ - `common` - Apache Common Log Format
502
+ - `short` - Shorter than common, includes response time
503
+ - `tiny` - Minimal output
504
+ - `dev` - Development-friendly colored output
505
+
506
+ ### Rate Limiting
507
+
508
+ Configurable rate limiting middleware with multiple store options.
509
+
510
+ ```javascript
511
+ const {createRateLimit, MemoryStore} = require('0http-bun/lib/middleware')
512
+
513
+ // Basic rate limiting
514
+ router.use(
515
+ createRateLimit({
516
+ windowMs: 15 * 60 * 1000, // 15 minutes
517
+ max: 100, // Max 100 requests per windowMs
518
+ }),
519
+ )
520
+
521
+ // Advanced configuration
522
+ router.use(
523
+ createRateLimit({
524
+ windowMs: 60 * 1000, // 1 minute
525
+ max: 20, // Max requests
526
+ keyGenerator: (req) => {
527
+ // Custom key generation (default: IP address)
528
+ return req.headers.get('x-user-id') || req.headers.get('x-forwarded-for')
529
+ },
530
+ skip: (req) => {
531
+ // Skip rate limiting for certain requests
532
+ return req.url.startsWith('/health')
533
+ },
534
+ handler: (req, totalHits, max, resetTime) => {
535
+ // Custom rate limit exceeded response
536
+ return Response.json(
537
+ {
538
+ error: 'Rate limit exceeded',
539
+ resetTime: resetTime.toISOString(),
540
+ retryAfter: Math.ceil((resetTime.getTime() - Date.now()) / 1000),
541
+ },
542
+ {status: 429},
543
+ )
544
+ },
545
+ standardHeaders: true, // Send X-RateLimit-* headers
546
+ excludePaths: ['/health', '/metrics'],
547
+ }),
548
+ )
549
+
550
+ // Custom store (for distributed systems)
551
+ router.use(
552
+ createRateLimit({
553
+ store: new MemoryStore(), // Built-in memory store
554
+ // Or implement custom store with increment() method
555
+ }),
556
+ )
557
+ ```
558
+
559
+ **TypeScript Usage:**
560
+
561
+ ```typescript
562
+ import {createRateLimit, MemoryStore} from '0http-bun/lib/middleware'
563
+ import type {RateLimitOptions, RateLimitStore} from '0http-bun/lib/middleware'
564
+
565
+ const rateLimitOptions: RateLimitOptions = {
566
+ windowMs: 15 * 60 * 1000, // 15 minutes
567
+ max: 100,
568
+ keyGenerator: (req) => {
569
+ return (
570
+ req.headers.get('x-user-id') ||
571
+ req.headers.get('x-forwarded-for') ||
572
+ 'anonymous'
573
+ )
574
+ },
575
+ standardHeaders: true,
576
+ excludePaths: ['/health', '/ping'],
577
+ }
578
+
579
+ router.use(createRateLimit(rateLimitOptions))
580
+
581
+ // Custom store implementation
582
+ class CustomStore implements RateLimitStore {
583
+ async increment(
584
+ key: string,
585
+ windowMs: number,
586
+ ): Promise<{totalHits: number; resetTime: Date}> {
587
+ // Custom implementation
588
+ return {totalHits: 1, resetTime: new Date(Date.now() + windowMs)}
589
+ }
590
+ }
591
+ ```
592
+
593
+ #### Sliding Window Rate Limiter
594
+
595
+ For more precise rate limiting, use the sliding window implementation that **prevents burst traffic** at any point in time:
596
+
597
+ ```javascript
598
+ const {createSlidingWindowRateLimit} = require('0http-bun/lib/middleware')
599
+
600
+ // Basic sliding window rate limiter
601
+ router.use(
602
+ createSlidingWindowRateLimit({
603
+ windowMs: 60 * 1000, // 1 minute sliding window
604
+ max: 10, // Max 10 requests per minute
605
+ keyGenerator: (req) => req.headers.get('x-forwarded-for') || 'default',
606
+ }),
607
+ )
608
+ ```
609
+
610
+ **TypeScript Usage:**
611
+
612
+ ```typescript
613
+ import {createSlidingWindowRateLimit} from '0http-bun/lib/middleware'
614
+ import type {RateLimitOptions} from '0http-bun/lib/middleware'
615
+
616
+ const slidingOptions: RateLimitOptions = {
617
+ windowMs: 60 * 1000, // 1 minute
618
+ max: 10, // 10 requests max
619
+ keyGenerator: (req) => req.user?.id || req.headers.get('x-forwarded-for'),
620
+ handler: (req, hits, max, resetTime) => {
621
+ return Response.json(
622
+ {
623
+ error: 'Rate limit exceeded',
624
+ retryAfter: Math.ceil((resetTime.getTime() - Date.now()) / 1000),
625
+ limit: max,
626
+ used: hits,
627
+ },
628
+ {status: 429},
629
+ )
630
+ },
631
+ }
632
+
633
+ router.use(createSlidingWindowRateLimit(slidingOptions))
634
+ ```
635
+
636
+ **How Sliding Window Differs from Fixed Window:**
637
+
638
+ The sliding window approach provides **more accurate and fair rate limiting** by tracking individual request timestamps:
639
+
640
+ - **Fixed Window**: Divides time into discrete chunks (e.g., 09:00:00-09:00:59, 09:01:00-09:01:59)
641
+ - ⚠️ **Problem**: Allows burst traffic at window boundaries (20 requests in 2 seconds)
642
+ - **Sliding Window**: Uses a continuous, moving time window from current moment
643
+ - ✅ **Advantage**: Prevents bursts at any point in time (true rate limiting)
644
+
645
+ **Use Cases for Sliding Window:**
646
+
647
+ ```javascript
648
+ // Financial API - Zero tolerance for payment bursts
649
+ router.use(
650
+ '/api/payments/*',
651
+ createSlidingWindowRateLimit({
652
+ windowMs: 60 * 1000, // 1 minute
653
+ max: 3, // Only 3 payment attempts per minute
654
+ keyGenerator: (req) => req.user.accountId,
655
+ }),
656
+ )
657
+
658
+ // User Registration - Prevent automated signups
659
+ router.use(
660
+ '/api/register',
661
+ createSlidingWindowRateLimit({
662
+ windowMs: 3600 * 1000, // 1 hour
663
+ max: 3, // 3 accounts per IP per hour
664
+ keyGenerator: (req) => req.headers.get('x-forwarded-for'),
665
+ }),
666
+ )
667
+
668
+ // File Upload - Prevent abuse
669
+ router.use(
670
+ '/api/upload',
671
+ createSlidingWindowRateLimit({
672
+ windowMs: 300 * 1000, // 5 minutes
673
+ max: 10, // 10 uploads per 5 minutes
674
+ keyGenerator: (req) => req.user.id,
675
+ }),
676
+ )
677
+ ```
678
+
679
+ **Performance Considerations:**
680
+
681
+ - **Memory Usage**: Higher than fixed window (stores timestamp arrays)
682
+ - **Time Complexity**: O(n) per request where n = requests in window
683
+ - **Best For**: Critical APIs, financial transactions, user-facing features
684
+ - **Use Fixed Window For**: High-volume APIs where approximate limiting is acceptable
685
+
686
+ **Advanced Configuration:**
687
+
688
+ ```typescript
689
+ // Tiered rate limiting based on user level
690
+ const createTieredRateLimit = (req) => {
691
+ const userTier = req.user?.tier || 'free'
692
+ const configs = {
693
+ free: {windowMs: 60 * 1000, max: 10},
694
+ premium: {windowMs: 60 * 1000, max: 100},
695
+ enterprise: {windowMs: 60 * 1000, max: 1000},
696
+ }
697
+ return createSlidingWindowRateLimit(configs[userTier])
698
+ }
699
+ ```
700
+
701
+ **Rate Limit Headers:**
702
+
703
+ Both rate limiters send the following headers when `standardHeaders: true`:
704
+
705
+ - `X-RateLimit-Limit` - Request limit
706
+ - `X-RateLimit-Remaining` - Remaining requests
707
+ - `X-RateLimit-Reset` - Reset time (Unix timestamp)
708
+ - `X-RateLimit-Used` - Used requests
709
+
710
+ **Error Handling:**
711
+
712
+ Rate limiting middleware allows errors to bubble up as proper HTTP 500 responses. If your `keyGenerator` function or custom `store.increment()` method throws an error, it will not be caught and masked - the error will propagate up the middleware chain for proper error handling.
713
+
714
+ ## Creating Custom Middleware
715
+
716
+ ### Basic Middleware
717
+
718
+ ```typescript
719
+ import {ZeroRequest, StepFunction} from '0http-bun'
720
+
721
+ const customMiddleware = (req: ZeroRequest, next: StepFunction) => {
722
+ // Pre-processing
723
+ req.ctx = req.ctx || {}
724
+ req.ctx.startTime = Date.now()
725
+
726
+ // Continue to next middleware/handler
727
+ const response = next()
728
+
729
+ // Post-processing (if needed)
730
+ return response
731
+ }
732
+
733
+ router.use(customMiddleware)
734
+ ```
735
+
736
+ ### Async Middleware
737
+
738
+ ```typescript
739
+ const asyncMiddleware = async (req: ZeroRequest, next: StepFunction) => {
740
+ // Async pre-processing
741
+ const user = await validateUserSession(req)
742
+ req.ctx = {user}
743
+
744
+ // Continue
745
+ const response = await next()
746
+
747
+ // Async post-processing
748
+ await logUserActivity(user, req.url)
749
+
750
+ return response
751
+ }
752
+ ```
753
+
754
+ ### Error Handling in Middleware
755
+
756
+ ```typescript
757
+ const errorHandlingMiddleware = async (
758
+ req: ZeroRequest,
759
+ next: StepFunction,
760
+ ) => {
761
+ try {
762
+ return await next()
763
+ } catch (error) {
764
+ console.error('Middleware error:', error)
765
+
766
+ // Return error response
767
+ return Response.json(
768
+ {
769
+ error: 'Internal server error',
770
+ message:
771
+ process.env.NODE_ENV === 'development' ? error.message : undefined,
772
+ },
773
+ {status: 500},
774
+ )
775
+ }
776
+ }
777
+ ```
778
+
779
+ ## Middleware Execution Order
780
+
781
+ Middlewares execute in the order they are registered:
782
+
783
+ ```typescript
784
+ router.use(middleware1) // Executes first
785
+ router.use(middleware2) // Executes second
786
+ router.use(middleware3) // Executes third
787
+
788
+ router.get('/test', handler) // Final handler
789
+ ```
790
+
791
+ ## Path-Specific Middleware
792
+
793
+ Apply middleware only to specific paths:
794
+
795
+ ```typescript
796
+ // API-only middleware
797
+ router.use('/api/*', jwtAuth({secret: 'api-secret'}))
798
+ router.use('/api/*', rateLimit({max: 1000}))
799
+
800
+ // Admin-only middleware
801
+ router.use('/admin/*', adminAuthMiddleware)
802
+ router.use('/admin/*', auditLogMiddleware)
803
+
804
+ // Public paths (no auth required)
805
+ router.get('/health', healthCheckHandler)
806
+ router.get('/metrics', metricsHandler)
807
+ ```
808
+
809
+ ## Best Practices
810
+
811
+ 1. **Order Matters**: Place security middleware (CORS, auth) before business logic
812
+ 2. **Error Handling**: Always handle errors in async middleware
813
+ 3. **Performance**: Use `skip` functions to avoid unnecessary processing
814
+ 4. **Context**: Use `req.ctx` to pass data between middlewares
815
+ 5. **Immutability**: Don't modify the original request object directly
816
+ 6. **Logging**: Log middleware errors for debugging
817
+ 7. **Testing**: Test middleware in isolation with mock requests
818
+
819
+ ## Examples
820
+
821
+ ### Complete Middleware Stack
822
+
823
+ ```typescript
824
+ const {
825
+ createCORS,
826
+ createLogger,
827
+ createBodyParser,
828
+ createJWTAuth,
829
+ createRateLimit,
830
+ } = require('0http-bun/lib/middleware')
831
+
832
+ const router = http()
833
+
834
+ // 1. CORS (handle preflight requests first)
835
+ router.use(
836
+ createCORS({
837
+ origin: process.env.ALLOWED_ORIGINS?.split(','),
838
+ credentials: true,
839
+ }),
840
+ )
841
+
842
+ // 2. Logging (log all requests)
843
+ router.use(createLogger({format: 'combined'}))
844
+
845
+ // 3. Rate limiting (protect against abuse)
846
+ router.use(
847
+ createRateLimit({
848
+ windowMs: 15 * 60 * 1000,
849
+ max: 1000,
850
+ }),
851
+ )
852
+
853
+ // 4. Body parsing (parse request bodies)
854
+ router.use(createBodyParser({limit: '10mb'}))
855
+
856
+ // 5. Authentication (protect API routes)
857
+ router.use(
858
+ '/api/*',
859
+ createJWTAuth({
860
+ secret: process.env.JWT_SECRET,
861
+ skip: (req) => req.url.includes('/api/public/'),
862
+ }),
863
+ )
864
+
865
+ // Routes
866
+ router.get('/api/public/status', () => Response.json({status: 'ok'}))
867
+ router.get('/api/protected/data', (req) => Response.json({user: req.user}))
868
+ ```
869
+
870
+ This middleware stack provides a solid foundation for most web applications with security, logging, and performance features built-in.