throttler icon indicating copy to clipboard operation
throttler copied to clipboard

No documentation on how to use GqlThrottlerGuard with Subscription

Open vytautas-pranskunas- opened this issue 2 years ago • 8 comments

Is there an existing issue that is already proposing this?

  • [X] I have searched the existing issues

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

I am using global throttler guard for my graphql api. But I get an error when I am adding subscriptions "Cannot read properties of undefined (reading 'ip')", It is thrown bu GqlThrottlerGuard

Describe the solution you'd like

It would be great to have section in documentation here: https://docs.nestjs.com/security/rate-limiting#graphql how to handle it. I had to dig into your github code to see that there is a @SkipThrottle() decorator

Teachability, documentation, adoption, migration strategy

No response

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


vytautas-pranskunas- avatar Aug 18 '22 07:08 vytautas-pranskunas-

GraphQL Subscriptions, like general websockets, act a little bit differently. Can you get me a reproduction with subscriptions enabled and I can work on the documentation of how to make it work with the throttler?

jmcdo29 avatar Aug 18 '22 14:08 jmcdo29

@jmcdo29 it is just a matter of @SkipThrottle() - which I have found by diging into your repo code or you can make something else for people who wish to Throttle subscription messages :)

Unfurtunutelly i have no time at this moment f time to build repro code. But it is a matter of 1 resolver and GQL module from Nest.JS.

I can drop you some code that might speed your repro:

@Module({
    imports: [
        ThrottlerModule.forRoot({
            ttl: 60,
            limit: 60,
        }),
        GraphQLModule.forRootAsync<ApolloDriverConfig>({
            driver: ApolloDriver,
            useFactory: () => ({
                playground: false,
                autoSchemaFile: 'schema.gql',
                installSubscriptionHandlers: true,
                plugins: [ApolloServerPluginLandingPageLocalDefault()],
                subscriptions: {
                    'subscriptions-transport-ws': true
                    
                },
                formatError(error: GraphQLError) { return exceptionFormatter(error); },
                context: async ({ req, res }) => {

                    return {
                      
                    };
                },
            }),
        }),
        
    ],
    providers: [
       
        {
            provide: APP_GUARD,
            useExisting: GqlThrottlerGuard,
        },
      
        GqlThrottlerGuard,
        
    ]
})
export class AppModule {
   
}

and

@Public()
// @SkipThrottle()
@Resolver()
export class SubscriptionsResolver {
    constructor(@Inject('PUB_SUB') private pubSub: PubSub) { }

    @Subscription(() => NotificationResponse)
    notification(
        @Root() notificationPayload: NotificationPayload,
        @Args() args: NotificationsSubscriptionArgs,
    ) {
        return this.pubSub.asyncIterator(SubscriptionTopic.notifications);
    }
}

vytautas-pranskunas- avatar Aug 18 '22 15:08 vytautas-pranskunas-

I am running into a similar issue where all Query, Mutation operations are fine but the GqlThrottlerGuard throws Cannot read property 'header' of undefined error for GraphqQL subscription. Do I need to be doing something different? Or is skipping the only option at the moment?

aliabbasrizvi avatar Oct 26 '22 23:10 aliabbasrizvi

@aliabbasrizvi you'd need to find a reliable way to allow for writing headers of the websocket. The error comes from around here. If you can get a reproduction I can probably find something to use here

jmcdo29 avatar Oct 26 '22 23:10 jmcdo29

For those who are facing the issue like @aliabbasrizvi, you just need to put context: ({ req, res }) => ({ req, res }) into your GraphQLModule configuration!

hhphuc12 avatar Dec 24 '22 12:12 hhphuc12

For those who are facing the issue like @aliabbasrizvi, you just need to put context: ({ req, res }) => ({ req, res }) into your GraphQLModule configuration!

Simply adding this to the GraphQLModule configuration works, maybe this should be added to the ReadMe and this problem should be closed @jmcdo29 :)

jirios125 avatar Sep 20 '23 04:09 jirios125

@jmcdo29 any update? Still do not know how to use GqlThrottlerGuard with Subscription. When use

@Injectable()
export class WsThrottlerGuard extends ThrottlerGuard {
  async handleRequest(context: ExecutionContext, limit: number, ttl: number): Promise<boolean> {
    const client = context.switchToWs().getClient();
    const ip = client._socket.remoteAddress;
    const key = this.generateKey(context, ip);
    const { totalHits } = await this.storageService.increment(key, ttl);

    if (totalHits > limit) {
      throw new ThrottlerException();
    }

    return true;
  }
}

Throw Cannot read properties of undefined (reading '_socket') if use

@Injectable()
export class GqlThrottlerGuard extends ThrottlerGuard {
 getRequestResponse(context: ExecutionContext) {
   const gqlCtx = GqlExecutionContext.create(context);
   const ctx = gqlCtx.getContext();
   return { req: ctx.req, res: ctx.res };
 }
}

Throw error Cannot read properties of undefined (reading 'ip')

Is there an example to set GqlThrottlerGuard with subscriptions

bellondr avatar Oct 11 '23 02:10 bellondr

As I said, if someone can get me a reproduction I can better look into it. I haven't tried to look into rate limiting subscriptions as I don't use gql much

jmcdo29 avatar Oct 11 '23 15:10 jmcdo29