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

Question - Throttler Per UserContext

ilanl opened this issue · comments

Let's say I have a socket server that receives on websocket a message

ADD_TASK
data: { userId: 1 }

I would like to throttle the usage , so I'm decorating with:

  @Throttle(2, 10)
  @SubscribeMessage('ADD_TASK')
  onAddTask(client: Socket, data: { userId: number }): void {
 ... do something only if throttling okay per userId (not per socketId)
  }

I was under the impression I could pass a function to the throttle to use the context on the request.
I don't want to throttle globally but per userId, but the event is the same for all.

I'm not sure I understand the question. You can set what tracker the guard is doing by overriding the handleRequest method which takes in the ExectuionContext, limit, and ttl and you can set up what to throttle by

Sounds good. I'll do that!

I'm not sure I understand the question. You can set what tracker the guard is doing by overriding the handleRequest method which takes in the ExectuionContext, limit, and ttl and you can set up what to throttle by

Sorry I didn't quite get that, Can you provide a minimal example?

It's kind of hard to give a general minimum example because the use case changes from person to person. But if you just need to rate limit based on something like user id and not per route you could do something like

@Injectable()
export class UserThrottlerGuard extends ThrottlerGuard {
  getTracker(req: Request) {
    return req.user.id;
  }
  generateKey(context: ExecutionContext, sufix: string) {
    return suffix;
  }
}

And now it's based on the userid instead of the class name, handler, and IP

commented

It would be nice if this feature can be added to the module, or at least mention it in the doc

PRs for the docs are always welcome at nestjs/docs.nestjs.com

For anyone trying to configure this on a single route (the other routes use a global default throttler), use

export const SkipGlobalThrottle = () => SetMetadata('skipGlobalThrottle', true);
@Injectable()
export class GlobalThrottleGuard extends ThrottlerGuard {
  async canActivate(context: ExecutionContext) {
    const skipGlobalThrottle = this.reflector.get<boolean>(
      'skipGlobalThrottle',
      context.getHandler(),
    );
    if (skipGlobalThrottle) {
      return true;
    }

    return super.canActivate(context);
  }
}
  @SkipGlobalThrottle() // we need to skip global throttle as the settings below apply to it instead
  @Throttle({
    default: {
      limit: 10,
      ttl: hours(1),
    },
  })
  @UseGuards(UserThrottlerGuard)