Installations
npm install nest-ratelimiter
Developer Guide
Typescript
Yes
Module System
CommonJS
Min. Node Version
>=18.0.0
Node Version
22.13.0
NPM Version
10.9.2
Releases
0.4.0: nestjs 11 support
Published on 23 Jan 2025
0.3.3: fix createErrorBody usage
Published on 23 Jun 2023
0.3.2: fix headers parsing
Published on 22 Jun 2023
0.3.1: fix packaging
Published on 20 Jun 2023
0.3.0: refactor; add service; update ci, deps, docs
Published on 20 Jun 2023
0.2.0: drop nestjs-redis as dep, add nestjs6+ support, move ratelimiter as peerDep
Published on 31 Aug 2021
Contributors
Languages
TypeScript (86.77%)
JavaScript (13.23%)
Developer
iamolegga
Download Statistics
Total Downloads
97,262
Last Day
61
Last Week
643
Last Month
1,804
Last Year
9,818
GitHub Statistics
54 Stars
1,543 Commits
6 Forks
3 Watching
1 Branches
3 Contributors
Bundle Size
8.69 kB
Minified
2.34 kB
Minified + Gzipped
Package Meta Information
Latest Version
0.4.0
Package Id
nest-ratelimiter@0.4.0
Unpacked Size
38.13 kB
Size
10.51 kB
File Count
29
NPM Version
10.9.2
Node Version
22.13.0
Publised On
23 Jan 2025
Total Downloads
Cumulative downloads
Total Downloads
97,262
Last day
-26.5%
61
Compared to previous day
Last week
80.1%
643
Compared to previous week
Last month
143.5%
1,804
Compared to previous month
Last year
-75.8%
9,818
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Peer Dependencies
5
Dev Dependencies
32
nest-ratelimiter
The most flexible NestJS rate limiter based on Redis (rate limit against not only req path but req body to block distributed brute force attacks).
Install
1npm i nest-ratelimiter ratelimiter @types/ratelimiter
If you want to use the default response when reaching a limit (text: "Rate limit exceeded, retry in human readable time value") also install ms
.
1npm i nest-ratelimiter ratelimiter @types/ratelimiter ms
Usage
Decorator
Let's start with controllers. Controllers are the places where you set parameters for the rate-limiter guard. You can set parameters for an entire controller or handler. Also, you can override the parameters of an entire controller by providing parameters for a specific handler. And finally, you can set several parameters for multi-checking.
1import { RateLimiter, LimiterInfo } from 'nest-ratelimiter'; 2 3// Let's define several functions that returns the identifier 4// to limit against. 5 6// This is functions for limiting requests by IP 7function getRequestIP(ctx: ExecutionContext) { 8 const request = ctx.switchToHttp().getRequest(); 9 return request.ip; 10} 11 12// Also you can limit every path separately 13function getRequestIPAndPath(ctx: ExecutionContext) { 14 const request = ctx.switchToHttp().getRequest(); 15 return `${request.ip}:${request.path}`; 16} 17 18// For blocking brute force attacks on login 19// you can return `login` value as identifier 20function getRequestBodyLogin(ctx: ExecutionContext) { 21 const request = ctx.switchToHttp().getRequest(); 22 return request.body.login; 23} 24 25// Now let's setup controller 26 27@Controller('/') 28// set params for entire controller 29@RateLimiter({ getId: getRequestIP }) 30class TestController { 31 // without providing params for specific handler 32 // it will inherit params of entire controller 33 @Get('some-api') 34 someApi() { 35 // ... 36 } 37 38 @Get('some-other-api') 39 // override params for specific handler 40 @RateLimiter({ 41 getId: getRequestIPAndPath, 42 max: 10, 43 duration: 10000, 44 }) 45 someOtherApi() { 46 // ... 47 } 48 49 @Get('one-more-api') 50 // turn off rate limiter for specific handler 51 @RateLimiter(false) 52 oneMoreApi() { 53 // ... 54 } 55 56 @Get('login') 57 // override params for specific handler 58 // by providing several params 59 @RateLimiter( 60 { 61 getId: getRequestIPAndPath, 62 max: 3, 63 duration: 60 * 60 * 1000, 64 }, 65 { 66 getId: getRequestBodyLogin, 67 max: 3, 68 duration: 60 * 60 * 1000, 69 // this is default `createErrorBody` function 70 // but you can set your own 71 createErrorBody: (limit: LimiterInfo) => { 72 const delta = limit.reset * 1000 - Date.now(); 73 // ms is imported from `ms` module 74 const readable = ms(delta, { long: true }); 75 return 'Rate limit exceeded, retry in ' + readable; 76 }, 77 }, 78 ) 79 login(creds: CredsDto) { 80 // ... 81 } 82}
Please, check out the docs of ratelimiter npm module for a better understanding of @RateLimiter
configuration.
Service
Another feature is using rate limiting in complex scenarios when id
could not be retrieved from request context. For example when it's required to make a request for id in 3rd party systems:
1import { 2 RATE_LIMITER_ASSERTER_TOKEN, 3 RateLimiterAsserter, 4 setHeaders, 5} from 'nest-ratelimiter' 6 7@Controller('/') 8class TestController { 9 constructor( 10 @Inject(RATE_LIMITER_ASSERTER_TOKEN) 11 private asserter: RateLimiterAsserter, 12 private db: DB; 13 ) {} 14 15 @Get('some-api') 16 someApi( 17 @Res({ passthrough: true }) response: any 18 ) { 19 const id = await this.db.getId(); 20 21 // this potentially throws `RateLimiterError` which is handled by internal 22 // filter and mapped to `TooManyRequestsException`. If that doesn't fit your 23 // needs, semply use filters, interceptors, try/catch to handle those errors 24 const limiterInfo = this.asserter.assert({ 25 id, 26 max: 10, 27 duration: 24 * 60 * 60 * 1000, 28 }); 29 30 // In this simple example limiterInfo is retrieved in controller and 31 // `X-RateLimit-...` headers could be easily set with `setHeaders` function. 32 // In a real world scenario this is done on a services layer and in a such 33 // case limiterInfo should be passed back to a controller where there is an 34 // access to underlying framework's response object. But this is optional 35 // and only required if there is a need for such headers in a positive case. 36 setHeaders(response, limiterInfo); 37 } 38}
Setup
Let's move to module registration.
As nest-ratelimiter
is using Redis as a data storage you have to provide an instance of Redis
client (redis
or ioredis
). As Redis client instantiation is out of the scope of this package, you can find something that fits your needs on npm or create your own module for NestJS. Here we will show two examples: with redis and nestjs-redis modules:
1import { RedisModule } from 'nestjs-redis';
2import { RateLimiterModule, LimiterInfo } from 'nest-ratelimiter';
3
4@Module({
5 imports: [
6
7 // redis example
8
9 RateLimiterModule.forRoot({
10
11 // The only required field is `db` (redis client), all the rest fields
12 // will be used as defaults for `@RateLimiter(...)` and RateLimiterAsserter
13 db: require("redis").createClient()
14
15 }),
16
17 // nestjs-redis example
18
19 RateLimiterModule.forRootAsync({
20
21 // 1 Register third-party module that provides `redis` or `ioredis` client
22 imports: [
23 RedisModule.register({
24 host: process.env.REDIS_HOST,
25 port: parseInt(process.env.REDIS_PORT),
26 db: parseInt(process.env.REDIS_DB),
27 }),
28 ],
29
30 // 2 And then inject redis client provider
31 inject: [RedisService],
32
33 // 3. build and return `RateLimiterModuleParams` from factory
34 useFactory: async (redisService: RedisService) => {
35
36 // You can set default fields for every @RateLimiter and then you don't
37 // have to copy-paste your params on entire codebase.
38
39 // IF YOU SET `getId` HERE, THEN ALL CONTROLLERS (EVEN THOSE WITHOUT
40 // @RateLimiter GUARD) WILL USE THIS FUNCTION BY DEFAULT. IF IN THAT
41 // CASE YOU NEED TO TURN OFF RATE LIMITER ON SOME SPECIFIC HANDLER OR
42 // CONTROLLER JUST USE `@RateLimiter(false)`
43
44 return {
45 db: redisService.getClient(),
46 max: 10,
47 duration: 10000,
48 getId: getRequestIPAndPath;
49 createErrorBody: (limit: LimiterInfo) => ({
50 error: {
51 code: 'MY-RATE-LIMIT-ERROR-CODE',
52 params: limit,
53 },
54 }),
55 };
56 },
57
58 }),
59 ],
60 controllers: [TestController],
61})
62class TestModule {}
Comparison with others
This nest-ratelimiter
is using TJ's ratelimiter package underhood, so it allows the creation of a flexible strategy for limiting not only per request path but per headers or body values or even asynchronously computed values on a services layer. It stores data only in redis
. If you need another store you can look at nestjs-rate-limiter, but it allows the use of strategies based on a request path only. Also, there is an example in official docs with setting up express-rate-limit middleware.
Migration
0.3.0
- no need to use
app.useGlobalGuards
as now it's set automatically - dropped support of nestjs < 8.0.0
- dropped support of node < 16.0.0
0.2.0
nestjs-redis
was moved from dependencies, now you are free to use any redis module that fit your needs, but you have to set new fieldRateLimiterModuleParams.db
that should beredis
orioredis
instance.ratelimiter
(with@types/ratelimiter
) was moved to peer dependencies. If you are usingnpm@7
it will install it automatically, either way you should install it manually.
Do you use this library?
Don't be shy to give it a star! ★
Also if you are into NestJS you might be interested in one of my other NestJS libs.
![Empty State](/_next/static/media/empty.e5fae2e5.png)
No vulnerabilities found.
Reason
13 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Reason
no binaries found in the repo
Reason
no dangerous workflow patterns detected
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: MIT License: LICENSE:0
Reason
1 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw
Reason
dependency not pinned by hash detected -- score normalized to 4
Details
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-lint-test.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/iamolegga/nestjs-ratelimiter/build-lint-test.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-lint-test.yml:35: update your workflow using https://app.stepsecurity.io/secureworkflow/iamolegga/nestjs-ratelimiter/build-lint-test.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-lint-test.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/iamolegga/nestjs-ratelimiter/build-lint-test.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish-alpha.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/iamolegga/nestjs-ratelimiter/publish-alpha.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish-alpha.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/iamolegga/nestjs-ratelimiter/publish-alpha.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish-coverage.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/iamolegga/nestjs-ratelimiter/publish-coverage.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish-coverage.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/iamolegga/nestjs-ratelimiter/publish-coverage.yml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/publish-coverage.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/iamolegga/nestjs-ratelimiter/publish-coverage.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish-with-git-tag-version.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/iamolegga/nestjs-ratelimiter/publish-with-git-tag-version.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish-with-git-tag-version.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/iamolegga/nestjs-ratelimiter/publish-with-git-tag-version.yml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/publish-with-git-tag-version.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/iamolegga/nestjs-ratelimiter/publish-with-git-tag-version.yml/master?enable=pin
- Warn: npmCommand not pinned by hash: .github/workflows/build-lint-test.yml:40
- Info: 0 out of 9 GitHub-owned GitHubAction dependencies pinned
- Info: 0 out of 2 third-party GitHubAction dependencies pinned
- Info: 3 out of 4 npmCommand dependencies pinned
Reason
Found 0/3 approved changesets -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
- Warn: no topLevel permission defined: .github/workflows/build-lint-test.yml:1
- Warn: no topLevel permission defined: .github/workflows/on-pr-master.yml:1
- Warn: no topLevel permission defined: .github/workflows/on-push-master.yml:1
- Warn: no topLevel permission defined: .github/workflows/on-release.yml:1
- Warn: no topLevel permission defined: .github/workflows/publish-alpha.yml:1
- Warn: no topLevel permission defined: .github/workflows/publish-coverage.yml:1
- Warn: no topLevel permission defined: .github/workflows/publish-with-git-tag-version.yml:1
- Info: no jobLevel write permissions found
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
branch protection not enabled on development/release branches
Details
- Warn: branch protection not enabled for branch 'master'
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 29 are checked with a SAST tool
Score
4.5
/10
Last Scanned on 2025-01-27
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More