throttler
throttler copied to clipboard
No documentation on how to use GqlThrottlerGuard with Subscription
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?
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 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);
}
}
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 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
For those who are facing the issue like @aliabbasrizvi, you just need to put context: ({ req, res }) => ({ req, res })
into your GraphQLModule configuration!
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 :)
@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
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