11import { Injectable , ExecutionContext , Logger , HttpException , HttpStatus } from '@nestjs/common' ;
2- import { ThrottlerGuard } from '@nestjs/throttler' ;
2+ import { ThrottlerGuard , ThrottlerLimitDetail } from '@nestjs/throttler' ;
33import { Request , Response } from 'express' ;
44
55/**
@@ -15,7 +15,10 @@ export class CustomThrottleGuard extends ThrottlerGuard {
1515 private readonly logger = new Logger ( CustomThrottleGuard . name ) ;
1616
1717 /** Called by ThrottlerGuard when the limit is exceeded. */
18- protected override throwThrottlingException ( context : ExecutionContext ) : Promise < void > {
18+ protected override async throwThrottlingException (
19+ context : ExecutionContext ,
20+ throttlerLimitDetail : ThrottlerLimitDetail ,
21+ ) : Promise < void > {
1922 const request = context . switchToHttp ( ) . getRequest < Request > ( ) ;
2023 const response = context . switchToHttp ( ) . getResponse < Response > ( ) ;
2124
@@ -24,21 +27,21 @@ export class CustomThrottleGuard extends ThrottlerGuard {
2427
2528 this . logger . warn ( `Rate limit exceeded: ip=${ ip } method=${ request . method } route=${ route } ` ) ;
2629
27- // Get throttle options from the decorator or default
28- const throttleOptions = this . getThrottleOptions ( context ) ;
29-
3030 // Inject standard rate-limit headers so clients can back off gracefully
31- response . setHeader ( 'Retry-After' , throttleOptions . ttl ) ;
32- response . setHeader ( 'X-RateLimit-Limit' , throttleOptions . limit ) ;
31+ // TTL in v6 is in seconds if defined that way in config, but throttlerLimitDetail.ttl is the value from config
32+ const ttlSeconds = throttlerLimitDetail . ttl ;
33+
34+ response . setHeader ( 'Retry-After' , ttlSeconds ) ;
35+ response . setHeader ( 'X-RateLimit-Limit' , throttlerLimitDetail . limit ) ;
3336 response . setHeader ( 'X-RateLimit-Remaining' , 0 ) ;
34- response . setHeader ( 'X-RateLimit-Reset' , Math . floor ( Date . now ( ) / 1000 ) + throttleOptions . ttl ) ;
37+ response . setHeader ( 'X-RateLimit-Reset' , Math . floor ( Date . now ( ) / 1000 ) + ttlSeconds ) ;
3538
3639 throw new HttpException (
3740 {
3841 statusCode : HttpStatus . TOO_MANY_REQUESTS ,
3942 error : 'Too Many Requests' ,
4043 message : 'You have exceeded the request rate limit. Please wait before retrying.' ,
41- retryAfterSeconds : throttleOptions . ttl ,
44+ retryAfterSeconds : ttlSeconds ,
4245 } ,
4346 HttpStatus . TOO_MANY_REQUESTS ,
4447 ) ;
@@ -49,20 +52,4 @@ export class CustomThrottleGuard extends ThrottlerGuard {
4952 if ( typeof forwarded === 'string' ) return forwarded . split ( ',' ) [ 0 ] . trim ( ) ;
5053 return request . ip ?? request . socket ?. remoteAddress ?? 'unknown' ;
5154 }
52-
53- private getThrottleOptions ( context : ExecutionContext ) : { limit : number ; ttl : number } {
54- // Try to get throttle options from the decorator
55- const handler = context . getHandler ( ) ;
56- const throttleDecorator = Reflect . getMetadata ( '__throttler__' , handler ) ;
57-
58- if ( throttleDecorator ) {
59- return {
60- limit : throttleDecorator . limit || 10 ,
61- ttl : throttleDecorator . ttl || 60 ,
62- } ;
63- }
64-
65- // Fallback to default options
66- return { limit : 10 , ttl : 60 } ;
67- }
6855}
0 commit comments