Socket
Socket
Sign inDemoInstall

@nestjs/throttler

Package Overview
Dependencies
Maintainers
2
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nestjs/throttler - npm Package Compare versions

Comparing version 3.1.0 to 4.0.0

dist/throttler-storage-options.interface.d.ts

31

CHANGELOG.md
# [2.0.0](https://github.com/nestjs/throttler/compare/v1.2.1...v2.0.0) (2021-07-09)
## 4.0.0
### Major Changes
- 4803dda: Rewrite the storage service to better handle large numbers of operations
## Why
The initial behavior was that `getRecord()` returned an list of sorted TTL
timestamps, then if it didn't reach the limit, it will call `addRecord()`.
This change was made based on the use of the Redis storage community package
where it was found how to prevent this issue. It was found out that
[express-rate-limit](https://github.com/express-rate-limit/express-rate-limit)
is incrementing a single number and returning the information in a single
roundtrip, which is significantly faster than how NestJS throttler works by
called `getRecord()`, then `addRecord`.
## Breaking Changes
- removed `getRecord`
- `addRecord(key: string, ttl: number): Promise<number[]>;` changes to `increment(key: string, ttl: number): Promise<ThrottlerStorageRecord>;`
## How to Migrate
If you are just _using_ the throttler library, you're already covered. No
changes necessary to your code, version 4.0.0 will work as is.
If you are providing a custom storage, you will need to remove your current
service's `getRecord` method and rename `addRecord` to `incremenet` while
adhering to the new interface and returning an `ThrottlerStorageRecord` object
## 3.1.0

@@ -4,0 +35,0 @@

7

dist/throttler-storage.interface.d.ts

@@ -0,6 +1,7 @@

import { ThrottlerStorageOptions } from './throttler-storage-options.interface';
import { ThrottlerStorageRecord } from './throttler-storage-record.interface';
export interface ThrottlerStorage {
storage: Record<string, number[]>;
getRecord(key: string): Promise<number[]>;
addRecord(key: string, ttl: number): Promise<void>;
storage: Record<string, ThrottlerStorageOptions>;
increment(key: string, ttl: number): Promise<ThrottlerStorageRecord>;
}
export declare const ThrottlerStorage: unique symbol;

@@ -62,12 +62,10 @@ "use strict";

const key = this.generateKey(context, tracker);
const ttls = await this.storageService.getRecord(key);
const nearestExpiryTime = ttls.length > 0 ? Math.ceil((ttls[0] - Date.now()) / 1000) : 0;
if (ttls.length >= limit) {
res.header('Retry-After', nearestExpiryTime);
const { totalHits, timeToExpire } = await this.storageService.increment(key, ttl);
if (totalHits > limit) {
res.header('Retry-After', timeToExpire);
this.throwThrottlingException(context);
}
res.header(`${this.headerPrefix}-Limit`, limit);
res.header(`${this.headerPrefix}-Remaining`, Math.max(0, limit - (ttls.length + 1)));
res.header(`${this.headerPrefix}-Reset`, nearestExpiryTime);
await this.storageService.addRecord(key, ttl);
res.header(`${this.headerPrefix}-Remaining`, Math.max(0, limit - totalHits));
res.header(`${this.headerPrefix}-Reset`, timeToExpire);
return true;

@@ -74,0 +72,0 @@ }

import { OnApplicationShutdown } from '@nestjs/common';
import { ThrottlerStorageOptions } from './throttler-storage-options.interface';
import { ThrottlerStorageRecord } from './throttler-storage-record.interface';
import { ThrottlerStorage } from './throttler-storage.interface';

@@ -6,6 +8,7 @@ export declare class ThrottlerStorageService implements ThrottlerStorage, OnApplicationShutdown {

private timeoutIds;
get storage(): Record<string, number[]>;
getRecord(key: string): Promise<number[]>;
addRecord(key: string, ttl: number): Promise<void>;
get storage(): Record<string, ThrottlerStorageOptions>;
private getExpirationTime;
private setExpirationTime;
increment(key: string, ttl: number): Promise<ThrottlerStorageRecord>;
onApplicationShutdown(): void;
}

@@ -19,13 +19,8 @@ "use strict";

}
async getRecord(key) {
return this.storage[key] || [];
getExpirationTime(key) {
return Math.floor((this.storage[key].expiresAt - Date.now()) / 1000);
}
async addRecord(key, ttl) {
const ttlMilliseconds = ttl * 1000;
if (!this.storage[key]) {
this.storage[key] = [];
}
this.storage[key].push(Date.now() + ttlMilliseconds);
setExpirationTime(key, ttlMilliseconds) {
const timeoutId = setTimeout(() => {
this.storage[key].shift();
this.storage[key].totalHits--;
clearTimeout(timeoutId);

@@ -36,2 +31,19 @@ this.timeoutIds = this.timeoutIds.filter((id) => id != timeoutId);

}
async increment(key, ttl) {
const ttlMilliseconds = ttl * 1000;
if (!this.storage[key]) {
this.storage[key] = { totalHits: 0, expiresAt: Date.now() + ttlMilliseconds };
}
let timeToExpire = this.getExpirationTime(key);
if (timeToExpire <= 0) {
this.storage[key].expiresAt = Date.now() + ttlMilliseconds;
timeToExpire = this.getExpirationTime(key);
}
this.storage[key].totalHits++;
this.setExpirationTime(key, ttlMilliseconds);
return {
totalHits: this.storage[key].totalHits,
timeToExpire,
};
}
onApplicationShutdown() {

@@ -38,0 +50,0 @@ this.timeoutIds.forEach(clearTimeout);

{
"name": "@nestjs/throttler",
"version": "3.1.0",
"version": "4.0.0",
"description": "A Rate-Limiting module for NestJS to work on Express, Fastify, Websockets, Socket.IO, and GraphQL, all rolled up into a simple package.",

@@ -51,51 +51,51 @@ "author": "Jay McDoniel <me@jaymcdoniel.dev>",

"devDependencies": {
"@changesets/cli": "2.25.0",
"@commitlint/cli": "17.1.2",
"@commitlint/config-angular": "17.1.0",
"@nestjs/cli": "9.1.4",
"@nestjs/common": "9.1.3",
"@nestjs/core": "9.1.3",
"@nestjs/graphql": "10.1.3",
"@nestjs/platform-express": "9.1.3",
"@nestjs/platform-fastify": "9.1.3",
"@nestjs/platform-socket.io": "9.1.3",
"@nestjs/platform-ws": "9.1.3",
"@nestjs/schematics": "9.0.3",
"@nestjs/testing": "9.1.3",
"@nestjs/websockets": "9.1.3",
"@changesets/cli": "2.26.0",
"@commitlint/cli": "17.4.2",
"@commitlint/config-angular": "17.4.2",
"@nestjs/cli": "9.1.8",
"@nestjs/common": "9.2.1",
"@nestjs/core": "9.2.1",
"@nestjs/graphql": "10.1.7",
"@nestjs/platform-express": "9.2.1",
"@nestjs/platform-fastify": "9.2.1",
"@nestjs/platform-socket.io": "9.2.1",
"@nestjs/platform-ws": "9.2.1",
"@nestjs/schematics": "9.0.4",
"@nestjs/testing": "9.2.1",
"@nestjs/websockets": "9.2.1",
"@semantic-release/git": "10.0.1",
"@types/express": "4.17.14",
"@types/express-serve-static-core": "4.17.31",
"@types/jest": "29.1.2",
"@types/express": "4.17.15",
"@types/express-serve-static-core": "4.17.32",
"@types/jest": "29.2.6",
"@types/md5": "2.3.2",
"@types/node": "16.11.65",
"@types/node": "18.11.18",
"@types/supertest": "2.0.12",
"@typescript-eslint/eslint-plugin": "5.40.0",
"@typescript-eslint/parser": "5.40.0",
"apollo-server-express": "3.10.3",
"apollo-server-fastify": "3.10.3",
"@typescript-eslint/eslint-plugin": "5.48.2",
"@typescript-eslint/parser": "5.48.2",
"apollo-server-express": "3.11.1",
"apollo-server-fastify": "3.11.1",
"conventional-changelog-cli": "2.2.2",
"cz-conventional-changelog": "3.3.0",
"eslint": "8.25.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-import": "2.26.0",
"eslint": "8.32.0",
"eslint-config-prettier": "8.6.0",
"eslint-plugin-import": "2.27.5",
"graphql": "16.6.0",
"graphql-tools": "8.3.6",
"husky": "8.0.1",
"jest": "29.1.2",
"lint-staged": "13.0.3",
"graphql-tools": "8.3.15",
"husky": "8.0.3",
"jest": "29.3.1",
"lint-staged": "13.1.0",
"nodemon": "2.0.20",
"pinst": "3.0.0",
"prettier": "2.7.1",
"prettier": "2.8.3",
"reflect-metadata": "0.1.13",
"rimraf": "3.0.2",
"rxjs": "7.5.7",
"socket.io": "4.5.2",
"supertest": "6.3.0",
"ts-jest": "29.0.3",
"ts-loader": "9.4.1",
"rimraf": "4.1.1",
"rxjs": "7.8.0",
"socket.io": "4.5.4",
"supertest": "6.3.3",
"ts-jest": "29.0.5",
"ts-loader": "9.4.2",
"ts-node": "10.9.1",
"tsconfig-paths": "4.1.0",
"typescript": "4.8.4",
"ws": "8.9.0"
"tsconfig-paths": "4.1.2",
"typescript": "4.9.4",
"ws": "8.12.0"
},

@@ -102,0 +102,0 @@ "peerDependencies": {

@@ -34,3 +34,2 @@ <p align="center">

## Installation

@@ -220,4 +219,4 @@

export interface ThrottlerStorage {
getRecord(key: string): Promise<number[]>;
addRecord(key: string, ttl: number): Promise<void>;
storage: Record<string, ThrottlerStorageOptions>;
increment(key: string, ttl: number): Promise<ThrottlerStorageRecord>;
}

@@ -264,9 +263,8 @@ ```

const key = this.generateKey(context, ip);
const ttls = await this.storageService.getRecord(key);
const { totalHits } = await this.storageService.increment(key, ttl);
if (ttls.length >= limit) {
if (totalHits > limit) {
throw new ThrottlerException();
}
await this.storageService.addRecord(key, ttl);
return true;

@@ -312,2 +310,2 @@ }

Nest is [MIT licensed](LICENSE).
Nest is [MIT licensed](LICENSE).

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc