@fastify/rate-limit
Version:
A low overhead rate limiter for your routes
59 lines (48 loc) • 1.85 kB
JavaScript
'use strict'
const lua = `
-- Key to operate on
local key = KEYS[1]
-- Time window for the TTL
local timeWindow = tonumber(ARGV[1])
-- Max requests
local max = tonumber(ARGV[2])
-- Flag to determine if TTL should be reset after exceeding
local continueExceeding = ARGV[3] == 'true'
--Flag to determine if exponential backoff should be applied
local exponentialBackoff = ARGV[4] == 'true'
--Max safe integer
local MAX_SAFE_INTEGER = (2^53) - 1
-- Increment the key's value
local current = redis.call('INCR', key)
if current == 1 or (continueExceeding and current > max) then
redis.call('PEXPIRE', key, timeWindow)
elseif exponentialBackoff and current > max then
local backoffExponent = current - max - 1
timeWindow = math.min(timeWindow * (2 ^ backoffExponent), MAX_SAFE_INTEGER)
redis.call('PEXPIRE', key, timeWindow)
else
timeWindow = redis.call('PTTL', key)
end
return {current, timeWindow}
`
function RedisStore (continueExceeding, exponentialBackoff, redis, key = 'fastify-rate-limit-') {
this.continueExceeding = continueExceeding
this.exponentialBackoff = exponentialBackoff
this.redis = redis
this.key = key
if (!this.redis.rateLimit) {
this.redis.defineCommand('rateLimit', {
numberOfKeys: 1,
lua
})
}
}
RedisStore.prototype.incr = function (ip, cb, timeWindow, max) {
this.redis.rateLimit(this.key + ip, timeWindow, max, this.continueExceeding, this.exponentialBackoff, (err, result) => {
err ? cb(err, null) : cb(null, { current: result[0], ttl: result[1] })
})
}
RedisStore.prototype.child = function (routeOptions) {
return new RedisStore(routeOptions.continueExceeding, routeOptions.exponentialBackoff, this.redis, `${this.key}${routeOptions.routeInfo.method}${routeOptions.routeInfo.url}-`)
}
module.exports = RedisStore