@api-guard/trafix
Version:
A NestJS guard for API rate limiting using Redis and sliding window algorithm
59 lines (47 loc) • 1.67 kB
text/typescript
import { Inject, Injectable } from '@nestjs/common';
import { RedisService } from '../memory-store';
import { IGuardConfig } from '../types';
import { RATE_LIMIT_CONFIG } from '../constant';
()
export class SlidingWindowService {
constructor(
private readonly redisService: RedisService,
(RATE_LIMIT_CONFIG) private readonly config: IGuardConfig
) {}
async validateAPIRequest(key: string): Promise<{
isAllowed: boolean;
remainingRequests: number;
}> {
const now = Date.now(); // Current timestamp
// Lua script for atomic execution
const luaScript = `
local key = KEYS[1]
local now = tonumber(ARGV[1])
local windowSize = tonumber(ARGV[2])
local limit = tonumber(ARGV[3])
-- Remove expired logs
redis.call('ZREMRANGEBYSCORE', key, 0, now - windowSize)
-- Get count of remaining requests in the current window
local requestCount = redis.call('ZCOUNT', key, now - windowSize, now)
if requestCount >= limit then
return requestCount
end
-- Log the request with current timestamp
redis.call('ZADD', key, now, tostring(now))
-- Ensure TTL for key expiry
redis.call('EXPIRE', key, math.ceil(windowSize / 1000))
return requestCount
`;
// Execute the Lua script
const requestCount: number =
await this.redisService.executeLuaScript<number>(
luaScript,
[key],
[now, this.config.windowMs * 1000, this.config.maxRequests]
);
return {
isAllowed: requestCount < this.config.maxRequests,
remainingRequests: this.config.maxRequests - requestCount,
};
}
}