@genezio/rate-limiter
Advanced tools
Comparing version 1.0.2 to 1.1.0
/** * | ||
* The type that defines the parameters of the GenezioRateLimiter decorator. | ||
* @param {string} dbUrl The URL of the Redis database. | ||
* @param {number} limit The maximum number of requests that can be made from the same sourceIp in a minute. | ||
* | ||
* @param {string} dbUrl The URL of the Redis database (default is redis://localhost:6379). | ||
* @param {number} limit The maximum number of requests that can be made from the same sourceIp in a minute (default is 50). | ||
* @param {number} refreshRate The rate at which the limit is refreshed in seconds (default is 59). | ||
*/ | ||
@@ -10,2 +10,3 @@ export type GenezioRateLimiterOptionsParameters = { | ||
limit?: number; | ||
refreshRate?: number; | ||
}; | ||
@@ -15,5 +16,5 @@ /** | ||
* @param {GenezioRateLimiterOptionsParameters} _dict An object that contains the parameters of the GenezioRateLimiter. | ||
* The object has the following structure: {dbUrl?: string, limit?: number} | ||
* The object has the following structure: {dbUrl?: string, limit?: number, refreshRate?: number} | ||
* @returns the async function that will be executed after the rate limiter is applied. | ||
*/ | ||
export declare function GenezioRateLimiter(_dict?: GenezioRateLimiterOptionsParameters): (value: Function, _context: any) => (...args: any[]) => Promise<any>; |
@@ -7,2 +7,3 @@ "use strict"; | ||
exports.GenezioRateLimiter = void 0; | ||
/* eslint-disable*/ | ||
const ioredis_1 = __importDefault(require("ioredis")); | ||
@@ -13,3 +14,3 @@ const types_1 = require("@genezio/types"); | ||
* @param {GenezioRateLimiterOptionsParameters} _dict An object that contains the parameters of the GenezioRateLimiter. | ||
* The object has the following structure: {dbUrl?: string, limit?: number} | ||
* The object has the following structure: {dbUrl?: string, limit?: number, refreshRate?: number} | ||
* @returns the async function that will be executed after the rate limiter is applied. | ||
@@ -21,11 +22,27 @@ */ | ||
try { | ||
redisClient = new ioredis_1.default(_dict.dbUrl ? _dict.dbUrl : "", { | ||
let retryCount = 0; | ||
redisClient = new ioredis_1.default(_dict.dbUrl ? _dict.dbUrl : "redis://localhost:6379", { | ||
retryStrategy: function () { | ||
return undefined; | ||
if (retryCount > 5) { | ||
return undefined; | ||
} | ||
return 1000; | ||
}, | ||
}); | ||
redisClient.on("error", function (error) { | ||
console.log("Could not connect to the Redis database. " + error); | ||
retryCount++; | ||
if (retryCount > 5) { | ||
console.log("Could not connect to the Redis database after 5 retries. Stopping polling."); | ||
redisClient.disconnect(); | ||
throw error; | ||
} | ||
}); | ||
redisClient.on("reconnecting", function () { | ||
console.log("Trying to reconnect to the Redis database..."); | ||
}); | ||
} | ||
catch (error) { | ||
console.log("Error when opperating on the redis client. Remember to set the Redis dbUrl parameter in the RateLimiter decorator."); | ||
console.log(error); | ||
console.log("Error when operating on the redis client. Remember to set the Redis dbUrl parameter in the GenezioRateLimiter decorator."); | ||
throw error; | ||
} | ||
@@ -36,28 +53,30 @@ return function (value, _context) { | ||
if (args.length === 0 || !args[0].isGnzContext) { | ||
console.log("Warning: the GenezioRateLimiter decorator must be used with the first parameter being a GnzContext object"); | ||
console.log("Error: the GenezioRateLimiter decorator must be used with the first parameter being a GnzContext object"); | ||
throw new types_1.GenezioError("Error: the GenezioRateLimiter decorator must be used with the first parameter being a GnzContext object", types_1.GenezioErrorCodes.BadRequest); | ||
} | ||
else { | ||
try { | ||
const date = new Date(); | ||
const oldCount = await redisClient.get(`${args[0].requestContext.http.sourceIp}:${date.getMinutes()}`); | ||
// If the limit is reached, throw an error | ||
if (oldCount && parseInt(oldCount) >= (_dict.limit ? _dict.limit : 50)) { | ||
throw new types_1.GenezioError("Rate limit exceeded", types_1.GenezioErrorCodes.RequestTimeout); | ||
} | ||
// Increment the count of requests made by the sourceIp in the current minute | ||
await redisClient | ||
.multi() | ||
.incr(`${args[0].requestContext.http.sourceIp}:${date.getMinutes()}`) | ||
.expire(`${args[0].requestContext.http.sourceIp}:${date.getMinutes()}`, 59) | ||
.exec(); | ||
try { | ||
const date = new Date(); | ||
const oldCount = await redisClient.get(`${args[0].requestContext.http.sourceIp}:${date.getMinutes()}`); | ||
// If the limit is reached, throw an error | ||
if (oldCount && parseInt(oldCount) >= (_dict.limit ? _dict.limit : 50)) { | ||
throw new types_1.GenezioError("Rate limit exceeded", types_1.GenezioErrorCodes.RequestTimeout); | ||
} | ||
catch (error) { | ||
// If the error is a RequestTimeout error, throw it to indicate that the request should be timed out | ||
if (error.code === types_1.GenezioErrorCodes.RequestTimeout) { | ||
throw error; | ||
} | ||
console.log("Error when opperating on the redis client. Remember to set the Redis dbUrl parameter in the GenezioRateLimiter decorator."); | ||
console.log(error); | ||
// If the refresh rate is less than 1 second, throw an error | ||
if (_dict.refreshRate && _dict.refreshRate < 1) { | ||
throw new types_1.GenezioError("The refresh rate must be at least 1 second", types_1.GenezioErrorCodes.BadRequest); | ||
} | ||
// Increment the count of requests made by the sourceIp in the current minute | ||
await redisClient | ||
.multi() | ||
.incr(`${args[0].requestContext.http.sourceIp}:${date.getMinutes()}`) | ||
.expire(`${args[0].requestContext.http.sourceIp}:${date.getMinutes()}`, _dict.refreshRate ? _dict.refreshRate : 59) | ||
.exec(); | ||
} | ||
catch (error) { | ||
// If the error is a RequestTimeout error, throw it to indicate that the request should be timed out | ||
if (error.code !== types_1.GenezioErrorCodes.RequestTimeout) { | ||
console.log("Error when operating on the redis client. Remember to set the Redis dbUrl parameter in the GenezioRateLimiter decorator."); | ||
} | ||
throw error; | ||
} | ||
// @ts-expect-error | ||
@@ -64,0 +83,0 @@ const func = value.bind(this); |
/** * | ||
* The type that defines the parameters of the GenezioRateLimiter decorator. | ||
* @param {string} dbUrl The URL of the Redis database. | ||
* @param {number} limit The maximum number of requests that can be made from the same sourceIp in a minute. | ||
* | ||
* @param {string} dbUrl The URL of the Redis database (default is redis://localhost:6379). | ||
* @param {number} limit The maximum number of requests that can be made from the same sourceIp in a minute (default is 50). | ||
* @param {number} refreshRate The rate at which the limit is refreshed in seconds (default is 59). | ||
*/ | ||
@@ -10,2 +10,3 @@ export type GenezioRateLimiterOptionsParameters = { | ||
limit?: number; | ||
refreshRate?: number; | ||
}; | ||
@@ -15,5 +16,5 @@ /** | ||
* @param {GenezioRateLimiterOptionsParameters} _dict An object that contains the parameters of the GenezioRateLimiter. | ||
* The object has the following structure: {dbUrl?: string, limit?: number} | ||
* The object has the following structure: {dbUrl?: string, limit?: number, refreshRate?: number} | ||
* @returns the async function that will be executed after the rate limiter is applied. | ||
*/ | ||
export declare function GenezioRateLimiter(_dict?: GenezioRateLimiterOptionsParameters): (value: Function, _context: any) => (...args: any[]) => Promise<any>; |
@@ -7,2 +7,3 @@ "use strict"; | ||
exports.GenezioRateLimiter = void 0; | ||
/* eslint-disable*/ | ||
const ioredis_1 = __importDefault(require("ioredis")); | ||
@@ -13,3 +14,3 @@ const types_1 = require("@genezio/types"); | ||
* @param {GenezioRateLimiterOptionsParameters} _dict An object that contains the parameters of the GenezioRateLimiter. | ||
* The object has the following structure: {dbUrl?: string, limit?: number} | ||
* The object has the following structure: {dbUrl?: string, limit?: number, refreshRate?: number} | ||
* @returns the async function that will be executed after the rate limiter is applied. | ||
@@ -21,11 +22,27 @@ */ | ||
try { | ||
redisClient = new ioredis_1.default(_dict.dbUrl ? _dict.dbUrl : "", { | ||
let retryCount = 0; | ||
redisClient = new ioredis_1.default(_dict.dbUrl ? _dict.dbUrl : "redis://localhost:6379", { | ||
retryStrategy: function () { | ||
return undefined; | ||
if (retryCount > 5) { | ||
return undefined; | ||
} | ||
return 1000; | ||
}, | ||
}); | ||
redisClient.on("error", function (error) { | ||
console.log("Could not connect to the Redis database. " + error); | ||
retryCount++; | ||
if (retryCount > 5) { | ||
console.log("Could not connect to the Redis database after 5 retries. Stopping polling."); | ||
redisClient.disconnect(); | ||
throw error; | ||
} | ||
}); | ||
redisClient.on("reconnecting", function () { | ||
console.log("Trying to reconnect to the Redis database..."); | ||
}); | ||
} | ||
catch (error) { | ||
console.log("Error when opperating on the redis client. Remember to set the Redis dbUrl parameter in the RateLimiter decorator."); | ||
console.log(error); | ||
console.log("Error when operating on the redis client. Remember to set the Redis dbUrl parameter in the GenezioRateLimiter decorator."); | ||
throw error; | ||
} | ||
@@ -36,28 +53,30 @@ return function (value, _context) { | ||
if (args.length === 0 || !args[0].isGnzContext) { | ||
console.log("Warning: the GenezioRateLimiter decorator must be used with the first parameter being a GnzContext object"); | ||
console.log("Error: the GenezioRateLimiter decorator must be used with the first parameter being a GnzContext object"); | ||
throw new types_1.GenezioError("Error: the GenezioRateLimiter decorator must be used with the first parameter being a GnzContext object", types_1.GenezioErrorCodes.BadRequest); | ||
} | ||
else { | ||
try { | ||
const date = new Date(); | ||
const oldCount = await redisClient.get(`${args[0].requestContext.http.sourceIp}:${date.getMinutes()}`); | ||
// If the limit is reached, throw an error | ||
if (oldCount && parseInt(oldCount) >= (_dict.limit ? _dict.limit : 50)) { | ||
throw new types_1.GenezioError("Rate limit exceeded", types_1.GenezioErrorCodes.RequestTimeout); | ||
} | ||
// Increment the count of requests made by the sourceIp in the current minute | ||
await redisClient | ||
.multi() | ||
.incr(`${args[0].requestContext.http.sourceIp}:${date.getMinutes()}`) | ||
.expire(`${args[0].requestContext.http.sourceIp}:${date.getMinutes()}`, 59) | ||
.exec(); | ||
try { | ||
const date = new Date(); | ||
const oldCount = await redisClient.get(`${args[0].requestContext.http.sourceIp}:${date.getMinutes()}`); | ||
// If the limit is reached, throw an error | ||
if (oldCount && parseInt(oldCount) >= (_dict.limit ? _dict.limit : 50)) { | ||
throw new types_1.GenezioError("Rate limit exceeded", types_1.GenezioErrorCodes.RequestTimeout); | ||
} | ||
catch (error) { | ||
// If the error is a RequestTimeout error, throw it to indicate that the request should be timed out | ||
if (error.code === types_1.GenezioErrorCodes.RequestTimeout) { | ||
throw error; | ||
} | ||
console.log("Error when opperating on the redis client. Remember to set the Redis dbUrl parameter in the GenezioRateLimiter decorator."); | ||
console.log(error); | ||
// If the refresh rate is less than 1 second, throw an error | ||
if (_dict.refreshRate && _dict.refreshRate < 1) { | ||
throw new types_1.GenezioError("The refresh rate must be at least 1 second", types_1.GenezioErrorCodes.BadRequest); | ||
} | ||
// Increment the count of requests made by the sourceIp in the current minute | ||
await redisClient | ||
.multi() | ||
.incr(`${args[0].requestContext.http.sourceIp}:${date.getMinutes()}`) | ||
.expire(`${args[0].requestContext.http.sourceIp}:${date.getMinutes()}`, _dict.refreshRate ? _dict.refreshRate : 59) | ||
.exec(); | ||
} | ||
catch (error) { | ||
// If the error is a RequestTimeout error, throw it to indicate that the request should be timed out | ||
if (error.code !== types_1.GenezioErrorCodes.RequestTimeout) { | ||
console.log("Error when operating on the redis client. Remember to set the Redis dbUrl parameter in the GenezioRateLimiter decorator."); | ||
} | ||
throw error; | ||
} | ||
// @ts-expect-error | ||
@@ -64,0 +83,0 @@ const func = value.bind(this); |
{ | ||
"name": "@genezio/rate-limiter", | ||
"version": "1.0.2", | ||
"version": "1.1.0", | ||
"description": "Rate limiter decorator for Genezio applications", | ||
@@ -5,0 +5,0 @@ "main": "./dist/cjs/index.js", |
17085
206