express-rate-limit-ioredis-store
Version:
A redis store implementation to be used alongside ioredis for the express-rate-limit middleware
63 lines (53 loc) • 1.82 kB
text/typescript
import {IncrementResponse, Options, Store} from 'express-rate-limit';
import Redis from 'ioredis';
type RedisStoreOptions = {
globalPrefix?: string;
client: Redis.Redis;
};
export default class ExpressRateLimitIORedisStore implements Store {
private client: Redis.Redis;
private globalPrefix: string;
private windowMs: number;
constructor({globalPrefix, client}: RedisStoreOptions) {
this.client = client;
this.globalPrefix = globalPrefix ?? 'express-rate-limit-store-';
this.windowMs = 0;
}
init(options: Options): void {
this.windowMs = options.windowMs;
}
async increment(key: string): Promise<IncrementResponse> {
const redisKey = this.getFullKey(key);
const totalHits = await this.client.incr(redisKey);
const insertionTime = await this.getInsertionTime(redisKey, totalHits);
const expiryTime = insertionTime + this.windowMs;
await this.client.pexpireat(redisKey, expiryTime);
return {
totalHits,
resetTime: new Date(expiryTime),
};
}
async decrement(key: string): Promise<void> {
const redisKey = this.getFullKey(key);
if (await this.client.get(redisKey)) {
await this.client.decr(redisKey);
}
}
async resetKey(key: string): Promise<void> {
await this.client.del(this.getFullKey(key));
}
private getFullKey(key: string) {
return this.globalPrefix + key;
}
private async getInsertionTime(key: string, value: number) {
const isKeyInsertion = value === 1;
if (isKeyInsertion) {
const now = new Date().getTime();
await this.client.set(key + '-create-time', now);
await this.client.pexpire(key + '-create-time', this.windowMs);
return now;
}
const createTime = await this.client.get(key + '-create-time');
return parseInt(createTime as string);
}
}