I'm developing an API using NestJS, GraphQL, and the @nestjs/throttler
package to limit request rates.
I implemented a custom GqlThrottlerGuard to adapt it for GraphQL, but I'm encountering the following error when making a query:
Error received
{
"errors": [
{
"message": "Cannot read properties of undefined (reading 'ip')",
"locations": [
{ "line": 2, "column": 3 }
],
"path": ["courses"],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"stacktrace": [
"TypeError: Cannot read properties of undefined (reading 'ip')",
" at ThrottlerGuard.getTracker (...)",
" at ThrottlerGuard.handleRequest (...)",
" at ThrottlerGuard.canActivate (...)"
]
}
}
],
"data": null
}
Despite this error, the rate limiting functionality does work as expected, because after 3 requests, I receive the expected error:
{
"errors": [
{
"message": "ThrottlerException: Too Many Requests"
}
]
}
Relevant Code
GraphQL Query
query Query {
courses
}
Resolver Decorator
@Query(() => String, { name: 'courses' })
@UseGuards(ThrottlerGuard)
findAll() {
return this.coursesService.findAll();
}
Service
findAll() {
return 'This action returns all courses';
}
ThrottlerModule Configuration
ThrottlerModule.forRoot([
{
limit: 3,
ttl: 10000,
},
]),
Custom GqlThrottlerGuard
@Injectable()
export class GqlThrottlerGuard extends ThrottlerGuard {
getRequestResponse(context: ExecutionContext) {
const gqlCtx = GqlExecutionContext.create(context);
const ctx = gqlCtx.getContext();
return { req: ctx.req, res: ctx.req.res };
}
}
Question
Why does ctx.req
seem to be defined (since rate limiting is working), but ThrottlerGuard
still tries to access ip
from an undefined
object? How can I fix this issue in NestJS with GraphQL?
Any help or suggestions would be greatly appreciated. Thanks!
What I tried:
I verified that rate-limiting works correctly because after 3 requests I get the "Too Many Requests" error.
I implemented a custom
GqlThrottlerGuard
to handle the GraphQL context, based on NestJS documentation.I added
trust proxy
inmain.ts
in case the issue was with the request’s IP:async function bootstrap() { const app = await NestFactory.create<NestExpressApplication>(AppModule); app.getHttpAdapter().getInstance().set('trust proxy', 'loopback'); await app.listen(3000); } bootstrap();
I tried extracting the IP in different ways in
getRequestResponse
:return { req: ctx.req, res: ctx.res }; // This error comes out: (Cannot read properties of undefined (reading 'header')) return { req: ctx.req, res: ctx.req.res }; // This error comes out: Cannot read properties of undefined (reading 'ip') return { req: ctx.req, res: ctx.req.res || ctx.req }; // This error comes out: Cannot read properties of undefined (reading 'ip')
I checked if
ctx.req
had theip
property, but it seemsctx.req
isundefined
.I tried overriding
getTracker()
inGqlThrottlerGuard
:protected async getTracker(req: Record<string, any>): Promise<string> { return req?.ips?.length ? req.ips[0] : req.ip; }
But it still fails with
req
beingundefined
.