@@ -173,7 +173,7 @@ router.use(
173
173
},
174
174
}),
175
175
)
176
- ` ` ` `
176
+ ` ` `
177
177
178
178
**TypeScript Usage:**
179
179
@@ -590,15 +590,127 @@ class CustomStore implements RateLimitStore {
590
590
}
591
591
` ` `
592
592
593
- ` ` ` `
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
+ ` ` `
594
700
595
701
**Rate Limit Headers:**
596
702
703
+ Both rate limiters send the following headers when ` standardHeaders: true ` :
704
+
597
705
- ` X - RateLimit- Limit` - Request limit
598
706
- ` X - RateLimit- Remaining` - Remaining requests
599
707
- ` X - RateLimit- Reset` - Reset time (Unix timestamp)
600
708
- ` X - RateLimit- Used` - Used requests
601
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
+
602
714
## Creating Custom Middleware
603
715
604
716
### Basic Middleware
@@ -619,7 +731,7 @@ const customMiddleware = (req: ZeroRequest, next: StepFunction) => {
619
731
}
620
732
621
733
router .use (customMiddleware)
622
- ` ` ` `
734
+ ` ` `
623
735
624
736
### Async Middleware
625
737
0 commit comments