DEV Community

Cover image for Rate Limiting in NestJS with GraphQL
Nikesh Dahal
Nikesh Dahal

Posted on

Rate Limiting in NestJS with GraphQL

Rate limiting is a critical security mechanism that protects your APIs from brute-force attacks, DDoS (Distributed Denial of Service) attempts, and abusive traffic. When working with a REST API in NestJS, standard Express middlewares like express-rate-limit work beautifully out of the box.

However, when you use GraphQL, all your requests (whether it's a login mutation or a data query) go through a single endpoint (e.g., /graphql or /cms-api). Because of this, standard HTTP route-based middleware can't differentiate between your operations.

In this post, we'll walk through how to implement granular, resolver-level rate limiting in a NestJS GraphQL application using the official @nestjs/throttler package.

Step 1: Install the Required Packages

To get started, install the @nestjs/throttler package:

npm install @nestjs/throttler
Enter fullscreen mode Exit fullscreen mode

Note: Depending on your NestJS version and peer dependencies, you might need to append --legacy-peer-deps.

Step 2: Create a Custom GraphQL Throttler Guard

By default, @nestjs/throttler is built for standard HTTP controllers. To make it understand the GraphQL context, we need to create a custom guard that overrides the getRequestResponse method.

Create a new file called gql-throttler.guard.ts (e.g., in src/guards/):

import { ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { ThrottlerGuard } from '@nestjs/throttler';

@Injectable()
export class GqlThrottlerGuard extends ThrottlerGuard {
  protected getRequestResponse(context: ExecutionContext) {
    const gqlCtx = GqlExecutionContext.create(context);
    const ctx = gqlCtx.getContext();

    // Map the GraphQL context to the standard req/res objects
    return { req: ctx.req, res: ctx.req.res || ctx.res };
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Register ThrottlerModule and Global Guard

Next, we need to import the ThrottlerModule into our main application module (app.module.ts) and register our custom guard globally so it protects all resolvers by default.

In this configuration, we'll define a default limit for all endpoints (e.g., 1000 requests per 15 minutes).

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { ThrottlerModule } from '@nestjs/throttler';
import { GqlThrottlerGuard } from './guards/gql-throttler.guard';
// ... other imports

@Module({
  imports: [
    ThrottlerModule.forRoot([
      {
        name: 'default',
        ttl: 15 * 60000, // 15 minutes in milliseconds
        limit: 1000,     // 1000 requests per 15 mins globally
      },
    ]),
    // ... other modules
  ],
  providers: [
    {
      provide: APP_GUARD,
      useClass: GqlThrottlerGuard, // Applies our custom guard globally
    },
    // ... other providers
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Step 4: Override Limits on Specific Resolvers

Now that the entire app is protected by a generous rate limit, what about sensitive endpoints like Authentication? We don't want to allow 1000 login attempts per 15 minutes!

We can use the @Throttle() decorator to easily override the default limits on specific resolvers (or individual queries/mutations).

Open your auth.resolver.ts and apply the decorator:

import { Resolver } from '@nestjs/graphql';
import { Throttle } from '@nestjs/throttler';

@Resolver()
// Override the 'default' throttler rule for this specific resolver
@Throttle({ default: { limit: 100, ttl: 15 * 60000 } }) 
export class AuthResolver {

  // All mutations and queries inside this resolver will now be restricted
  // to 100 requests per 15 minutes per IP address.

  // ... your login/register methods
}
Enter fullscreen mode Exit fullscreen mode

Summary

With this setup, you have achieved:

  1. Global Protection: Every GraphQL query and mutation is automatically protected against abuse.
  2. Context Awareness: The custom GqlThrottlerGuard safely parses GraphQL requests.
  3. Granular Control: You can easily tighten limits on sensitive operations like authentication using a single decorator.

Happy secure coding!

Top comments (0)