nestjs / throttler

A rate limiting module for NestJS to work with Fastify, Express, GQL, Websockets, and RPC 🧭

Home Page:https://nestjs.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Context-based `ttl` and `limit` options

benjlevesque opened this issue · comments

Is there an existing issue that is already proposing this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe it

The current limit and ttl options are statically configured, which prevents adjusting limits for specific users or IPs.

The existing skipIf is a workaround but isn't sufficient as it completely disables the throttling.

Describe the solution you'd like

Enable configuring the limit and ttl with a callback, like this:

ThrottlerModule.forRoot({
      limit: () => 5,
      ttl: () => 60,
 }),

or

ThrottlerModule.forRoot({
      limit: async (context) => // ... retrieve something,
      ttl: async (context) => 60,
}),

and, for the decorator:

@Throttle(()=> 2, ()=> 10)

NB: for my current use case, the ttl is not mandatory; neither is the fact that these callbacks return Promises, but it seems reasonable.

Teachability, documentation, adoption, migration strategy

No response

What is the motivation / use case for changing the behavior?

Per-user / per-application rate limiting: normal users have a specific rate, and automations (machine to machine integrations) have a different rate.

I think I'll take the work from #1484 (thanks for that) and merge it into a branch I'll be working on for the next major update as we'll then allow for multiple time spans as well, so just making all the breaking changes at once.

hey @jmcdo29. OK that's great. What breaking change are you referring to? The change in #1484 doesn't break the API I think?

Somehow I missed that you made that to where the functional approach works with the static one. Nevermind on me saying it's a breaking change

When will be the next major update? Hope this merges soon.

@benjlevesque
I tried to use skipIf like below. But It is now allowed to use await in skipIf. Can you share what you do to solve this problem?

async skipIf(context) {
  // TODO: if user is premium user, skip throttling
  const request = context.switchToHttp().getRequest();
  const extractor = ExtractJwt.fromAuthHeaderAsBearerToken();
  const accessToken = ExtractJwt.fromExtractors([extractor])(request);
  console.log("THROTTLE:", accessToken);
  const payload = jwtService.decode(accessToken);
  const userId = payload.sub;
  const user = await usersService.findOne(userId);
  console.log("THROTTLE:", user);
  return false;
},
        ```

Published in 4.2.0

Working on version 5.0.0 currently which will expand on this to allow for multiple throttler contexts as well, so you should then be able to set up multiple steps of throttling. The tests for this are "fun" to write 😆

You don't want a database operation happening on your throttler guard, as it's going to introduce unnecessary bottleneck.

What you can do is, read whatever user information you want from your Cache (you can use Redis for this)