Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@nestjs/throttler

nestjs1.3mMIT6.3.0TypeScript support: included

A Rate-Limiting module for NestJS to work on Express, Fastify, Websockets, Socket.IO, and GraphQL, all rolled up into a simple package.

nestjs, rate-limit, throttle, express, fastify, ws, gql, nest

readme

Nest Logo

A progressive Node.js framework for building efficient and scalable server-side applications.

NPM Version Package License NPM Downloads Coverage Discord Backers on Open Collective Sponsors on Open Collective

Description

A Rate-Limiter for NestJS, regardless of the context.

For an overview of the community storage providers, see Community Storage Providers.

This package comes with a couple of goodies that should be mentioned, first is the ThrottlerModule.

Installation

$ npm i --save @nestjs/throttler

Versions

@nestjs/throttler@^1 is compatible with Nest v7 while @nestjs/throttler@^2 is compatible with Nest v7 and Nest v8, but it is suggested to be used with only v8 in case of breaking changes against v7 that are unseen.

For NestJS v10, please use version 4.1.0 or above

Table of Contents

Usage

ThrottlerModule

Once the installation is complete, the ThrottlerModule can be configured as any other Nest package with forRoot or forRootAsync methods.

@@filename(app.module)
@Module({
  imports: [
    ThrottlerModule.forRoot([{
      ttl: 60000,
      limit: 10,
    }]),
  ],
})
export class AppModule {}

The above will set the global options for the ttl, the time to live in milliseconds, and the limit, the maximum number of requests within the ttl, for the routes of your application that are guarded.

Once the module has been imported, you can then choose how you would like to bind the ThrottlerGuard. Any kind of binding as mentioned in the guards section is fine. If you wanted to bind the guard globally, for example, you could do so by adding this provider to any module:

{
  provide: APP_GUARD,
  useClass: ThrottlerGuard
}

Multiple Throttler Definitions

There may come upon times where you want to set up multiple throttling definitions, like no more than 3 calls in a second, 20 calls in 10 seconds, and 100 calls in a minute. To do so, you can set up your definitions in the array with named options, that can later be referenced in the @SkipThrottle() and @Throttle() decorators to change the options again.

@@filename(app.module)
@Module({
  imports: [
    ThrottlerModule.forRoot([
      {
        name: 'short',
        ttl: 1000,
        limit: 3,
      },
      {
        name: 'medium',
        ttl: 10000,
        limit: 20
      },
      {
        name: 'long',
        ttl: 60000,
        limit: 100
      }
    ]),
  ],
})
export class AppModule {}

Customization

There may be a time where you want to bind the guard to a controller or globally, but want to disable rate limiting for one or more of your endpoints. For that, you can use the @SkipThrottle() decorator, to negate the throttler for an entire class or a single route. The @SkipThrottle() decorator can also take in an object of string keys with boolean values for if there is a case where you want to exclude most of a controller, but not every route, and configure it per throttler set if you have more than one. If you do not pass an object, the default is to use {{ '{' }} default: true {{ '}' }}

@SkipThrottle()
@Controller('users')
export class UsersController {}

This @SkipThrottle() decorator can be used to skip a route or a class or to negate the skipping of a route in a class that is skipped.

@SkipThrottle()
@Controller('users')
export class UsersController {
  // Rate limiting is applied to this route.
  @SkipThrottle({ default: false })
  dontSkip() {
    return 'List users work with Rate limiting.';
  }
  // This route will skip rate limiting.
  doSkip() {
    return 'List users work without Rate limiting.';
  }
}

There is also the @Throttle() decorator which can be used to override the limit and ttl set in the global module, to give tighter or looser security options. This decorator can be used on a class or a function as well. With version 5 and onwards, the decorator takes in an object with the string relating to the name of the throttler set, and an object with the limit and ttl keys and integer values, similar to the options passed to the root module. If you do not have a name set in your original options, use the string default You have to configure it like this:

// Override default configuration for Rate limiting and duration.
@Throttle({ default: { limit: 3, ttl: 60000 } })
@Get()
findAll() {
  return "List users works with custom rate limiting.";
}

Proxies

If your application runs behind a proxy server, check the specific HTTP adapter options (express and fastify) for the trust proxy option and enable it. Doing so will allow you to get the original IP address from the X-Forwarded-For header, and you can override the getTracker() method to pull the value from the header rather than from req.ip. The following example works with both express and fastify:

// throttler-behind-proxy.guard.ts
import { ThrottlerGuard } from '@nestjs/throttler';
import { Injectable } from '@nestjs/common';

@Injectable()
export class ThrottlerBehindProxyGuard extends ThrottlerGuard {
  protected getTracker(req: Record<string, any>): Promise<string> {
  return new Promise<string>((resolve, reject) => {
    const tracker = req.ips.length > 0 ? req.ips[0] : req.ip; // individualize IP extraction to meet your own needs
    resolve(tracker);
  });
  }
}

// app.controller.ts
import { ThrottlerBehindProxyGuard } from './throttler-behind-proxy.guard';

@UseGuards(ThrottlerBehindProxyGuard)

info Hint You can find the API of the req Request object for express here and for fastify here.

Websockets

This module can work with websockets, but it requires some class extension. You can extend the ThrottlerGuard and override the handleRequest method like so:

@Injectable()
export class WsThrottlerGuard extends ThrottlerGuard {
  async handleRequest(requestProps: ThrottlerRequest): Promise<boolean> {
    const { context, limit, ttl, throttler, blockDuration, getTracker, generateKey } = requestProps;

    const client = context.switchToWs().getClient();
    const tracker = client._socket.remoteAddress;
    const key = generateKey(context, tracker, throttler.name);
    const { totalHits, timeToExpire, isBlocked, timeToBlockExpire } =
      await this.storageService.increment(key, ttl, limit, blockDuration, throttler.name);

    const getThrottlerSuffix = (name: string) => (name === 'default' ? '' : `-${name}`);

    // Throw an error when the user reached their limit.
    if (isBlocked) {
      await this.throwThrottlingException(context, {
        limit,
        ttl,
        key,
        tracker,
        totalHits,
        timeToExpire,
        isBlocked,
        timeToBlockExpire,
      });
    }

    return true;
  }
}

info Hint If you are using ws, it is necessary to replace the _socket with conn

There's a few things to keep in mind when working with WebSockets:

  • Guard cannot be registered with the APP_GUARD or app.useGlobalGuards()
  • When a limit is reached, Nest will emit an exception event, so make sure there is a listener ready for this

info Hint If you are using the @nestjs/platform-ws package you can use client._socket.remoteAddress instead.

GraphQL

The ThrottlerGuard can also be used to work with GraphQL requests. Again, the guard can be extended, but this time the getRequestResponse method will be overridden

@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 };
  }
}

However, when using Apollo Express/Fastify or Mercurius, it's important to configure the context correctly in the GraphQLModule to avoid any problems.

Apollo Server (for Express):

For Apollo Server running on Express, you can set up the context in your GraphQLModule configuration as follows:

GraphQLModule.forRoot({
  // ... other GraphQL module options
  context: ({ req, res }) => ({ req, res }),
});

Apollo Server (for Fastify) & Mercurius:

When using Apollo Server with Fastify or Mercurius, you need to configure the context differently. You should use request and reply objects. Here's an example:

GraphQLModule.forRoot({
  // ... other GraphQL module options
  context: (request, reply) => ({ request, reply }),
});

Configuration

The following options are valid for the object passed to the array of the ThrottlerModule's options:

name the name for internal tracking of which throttler set is being used. Defaults to `default` if not passed
ttl the number of milliseconds that each request will last in storage
limit the maximum number of requests within the TTL limit
blockDuration the number of milliseconds that request will be blocked for that time
ignoreUserAgents an array of regular expressions of user-agents to ignore when it comes to throttling requests
skipIf a function that takes in the ExecutionContext and returns a boolean to short circuit the throttler logic. Like @SkipThrottler(), but based on the request
getTracker a function that takes in the Request and returns a string to override the default logic of the getTracker method
generateKey a function that takes in the ExecutionContext, the tacker string and the throttler name as a string and returns a string to override the final key which will be used to store the rate limit value. This overrides the default logic of the generateKey method

If you need to set up storages instead, or want to use a some of the above options in a more global sense, applying to each throttler set, you can pass the options above via the throttlers option key and use the below table

storage a custom storage service for where the throttling should be kept track. See here.
ignoreUserAgents an array of regular expressions of user-agents to ignore when it comes to throttling requests
skipIf a function that takes in the ExecutionContext and returns a boolean to short circuit the throttler logic. Like @SkipThrottler(), but based on the request
throttlers an array of throttler sets, defined using the table above
errorMessage a string OR a function that takes in the ExecutionContext and the ThrottlerLimitDetail and returns a string which overrides the default throttler error message
getTracker a function that takes in the Request and returns a string to override the default logic of the getTracker method
generateKey a function that takes in the ExecutionContext, the tacker string and the throttler name as a string and returns a string to override the final key which will be used to store the rate limit value. This overrides the default logic of the generateKey method

Async Configuration

You may want to get your rate-limiting configuration asynchronously instead of synchronously. You can use the forRootAsync() method, which allows for dependency injection and async methods.

One approach would be to use a factory function:

@Module({
  imports: [
    ThrottlerModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => [
        {
          ttl: config.get('THROTTLE_TTL'),
          limit: config.get('THROTTLE_LIMIT'),
        },
      ],
    }),
  ],
})
export class AppModule {}

You can also use the useClass syntax:

@Module({
  imports: [
    ThrottlerModule.forRootAsync({
      imports: [ConfigModule],
      useClass: ThrottlerConfigService,
    }),
  ],
})
export class AppModule {}

This is doable, as long as ThrottlerConfigService implements the interface ThrottlerOptionsFactory.

Storages

The built in storage is an in memory cache that keeps track of the requests made until they have passed the TTL set by the global options. You can drop in your own storage option to the storage option of the ThrottlerModule so long as the class implements the ThrottlerStorage interface.

info Note ThrottlerStorage can be imported from @nestjs/throttler.

Time Helpers

There are a couple of helper methods to make the timings more readable if you prefer to use them over the direct definition. @nestjs/throttler exports five different helpers, seconds, minutes, hours, days, and weeks. To use them, simply call seconds(5) or any of the other helpers, and the correct number of milliseconds will be returned.

Migration Guide

For most people, wrapping your options in an array will be enough.

If you are using a custom storage, you should wrap you ttl and limit in an array and assign it to the throttlers property of the options object.

Any @ThrottleSkip() should now take in an object with string: boolean props. The strings are the names of the throttlers. If you do not have a name, pass the string 'default', as this is what will be used under the hood otherwise.

Any @Throttle() decorators should also now take in an object with string keys, relating to the names of the throttler contexts (again, 'default' if no name) and values of objects that have limit and ttl keys.

Warning Important The ttl is now in milliseconds. If you want to keep your ttl in seconds for readability, use the seconds helper from this package. It just multiplies the ttl by 1000 to make it in milliseconds.

For more info, see the Changelog

Community Storage Providers

Feel free to submit a PR with your custom storage provider being added to this list.

License

Nest is MIT licensed.

🔼 Back to TOC

changelog

5.0.0

6.4.0

Minor Changes

  • 5cb4254: Update to allow for support for Nest version 11

6.3.0

Minor Changes

  • fc93f3a: pass context to getTraker as a second arg

6.2.1

Patch Changes

  • fbf27c6: Add the guard interfaces for export for public use

6.2.0

Minor Changes

  • 3d1a9a5: Swap MD5 hash for SHA256 to better support OpenSSL 3.0 and future iterations

6.1.1

Patch Changes

  • ef69348: Update the readme for websockets

6.1.0

Minor Changes

  • e058d50: Use ceil instead of floor while calculating expire and block expire at to properly account for rounding up instead of down and accidentally allowing for early continued requests. Related to #2074

6.0.0

Major Changes

  • 93b62d2: A time will be provided to block the request separately from the ttl. There is a breaking change at the library level. Storage library owners will be affected by this breaking change
  • 9b3f9cd: - e17a5dc: The storage has been updated to utilize Map instead of a simple object for key-value storage. This enhancement offers improved performance, especially for scenarios involving frequent additions and deletions of keys. There is a breaking change at the library level. Storage library owners will be affected by this breaking change

5.2.0

Minor Changes

  • 16467c1: Add dynamic error messages based on context and ThrottlerLimitDetail

5.1.2

Patch Changes

  • 7a431e5: Improve performance by replacing md5 npm package with Node.js crypto module.

5.1.1

Patch Changes

  • b06a208: Resolves a bug that cause 'this' to be undefined in the 'getTracker' and 'generateKey' methods of the custom ThrottlerGuard

5.1.0

Minor Changes

  • 903d187: Allow for throttler definitions to define their own trackers and key generators to allow for more customization of the rate limit process

5.0.1

Patch Changes

  • bc9e6b2: Correctly assign metadata for multiple throttlers passed to @SkipThrottle()

Major Changes

  • 2f4f2a7: # FEATURES

    • allow for multiple Throttler Contexts
    • allow for conditionally skipping based on ThrottleGuard#shouldSkip method
    • allow for easily overriding throttler message based on guard method
    • extra context passed to throw method for better customization of message
    • ThrottlerStorage no longer needs a storage property`
    • getTracker can now be async

    BREAKING CHANGES

    • ttl is now in milliseconds, not seconds, but there are time helper exposed to ease the migration to that
    • the module options is now either an array or an object with a throttlers array property
    • @Throttle() now takes in an object instead of two parameters, to allow for setting multiple throttle contexts at once in a more readable manner
    • @ThrottleSkip() now takes in an object with string boolean to say which throttler should be skipped
    • ttl and limit are no longer optional in the module's options. If an option object is passed, it must define the defaults for that throttler

    HOW TO MIGRATE

    For most people, wrapping your options in an array will be enough.

    If you are using a custom storage, you should wrap you ttl and limit in an array and assign it to the throttlers property of the options object.

    Any @ThrottleSkip() should now take in an object with string: boolean props. The strings are the names of the throttlers. If you do not have a name, pass the string 'default', as this is what will be used under the hood otherwise.

    Any @Throttle() decorators should also now take in an object with string keys, relating to the names of the throttler contexts (again, 'default' if no name) and values of objects that have limit and ttl keys.

    IMPORTANT: The ttl is now in miliseconds. If you want to keep your ttl in seconds for readability, usethe seconds helper from this package. It just multiplies the ttl by 1000 to make it in milliseconds.

4.2.1

Patch Changes

  • b72c9cb: Revert resolvable properties for ttl and limit

    The resolvable properties made a breaking change for custom guards that was unforseen. This reverts it and schedules the changes for 5.0.0 instead

4.2.0

Minor Changes

  • d8d8c93: Allow for ttl and limit to be set based on the execution context, instead of statically assigned for the entire application

4.1.0

Minor Changes

  • 527d51c: Support Nest v10

4.0.0

Major Changes

  • 4803dda: Rewrite the storage service to better handle large numbers of operations

    Why

    The initial behavior was that getRecord() returned an list of sorted TTL timestamps, then if it didn't reach the limit, it will call addRecord(). This change was made based on the use of the Redis storage community package where it was found how to prevent this issue. It was found out that express-rate-limit is incrementing a single number and returning the information in a single roundtrip, which is significantly faster than how NestJS throttler works by called getRecord(), then addRecord.

    Breaking Changes

    • removed getRecord
    • addRecord(key: string, ttl: number): Promise<number[]>; changes to increment(key: string, ttl: number): Promise<ThrottlerStorageRecord>;

    How to Migrate

    If you are just using the throttler library, you're already covered. No changes necessary to your code, version 4.0.0 will work as is.

    If you are providing a custom storage, you will need to remove your current service's getRecord method and rename addRecord to incremenet while adhering to the new interface and returning an ThrottlerStorageRecord object

3.1.0

Minor Changes

  • da3c950: Add skipIf option to throttler module options

    With the new option, you can pass a factory to skipIf and determine if the throttler guard should be used in the first palce or not. This acts just like applying @SkipThrottle() to every route, but can be customized to work off of the process.env or ExecutionContext object to provide better support for dev and QA environments.

3.0.0

Major Changes

  • c9fcd51: Upgrade nest version to v9. No breaking changes in direct code, but in nest v9 upgrade

2.0.1

Patch Changes

  • cf50808: fix memory leak for timeoutIds array. Before this, the timeoutIds array would not be trimmed and would grow until out of memory. Now ids are properly removed on timeout.

Features

  • adding in a comment about version (b13bf53)

BREAKING CHANGES

  • v2 and above is now being developed specificially for nest v8 and could have some unforseen side effects with Nest v7. use with v7 at your own risk.

1.2.1 (2021-07-09)

Performance Improvements

0.3.0 (2020-11-10)

Bug Fixes

  • module: async register is now forRootAsync (a1c6ace)

0.2.3 (2020-08-06)

Features

  • ws: allows for optional use of @nestjs/websocket (f437614)

0.2.2 (2020-06-12)

Bug Fixes

  • moves userAgent check to http handler (87183af)

Features

  • decorator: add setThrottlerMetadata() function back (ea31a9c)
  • graphql: implements graphql limiter (40eaff1)
  • Add support for ws (websockets) (a745295)
  • Add support for ws (websockets) (8103a5a)
  • Make storage methods async (92cd4eb)
  • exception: Use const instead of duplicated string (f95da2c)
  • guard: Add default case for context.getType() switch (ff46d57)
  • Implement basic support for websocket (3a0cf2e)

0.2.1 (2020-06-09)

Features

  • add support for ignoreUserAgents option (1ab5e17)

0.2.0 (2020-06-09)

Bug Fixes

  • make core module global and export core module inside ThrottlerModule (1f4df42)

Features

  • makes options required in forRoot and forRootAsync (14e272a)
  • remove global guard and require user to implement it manually (840eae4)

0.1.1 (2020-06-07)

Bug Fixes

  • interface: fixes the storage interface to be async (f7565d9)

0.1.0 (2020-06-07)

Bug Fixes

  • adds back AppModule to allow for running server for tests (5af229b)
  • updates some types (b26fc06)
  • updates storage interface to use number (339f29c)
  • updates tests and resolves comments from pr (ee87e05)
  • .gitignore: Ignore all dist and node_modules rather than root-level only (d9609af)
  • guard: Change RateLimit header prefix to X-RateLimit (328c0a3)
  • guard: Change RateLimit header prefix to X-RateLimit (3903885)
  • guard: guard now binds globally without the use of @UseGuards() (4022447)
  • guard: guard now binds globally without the use of @UseGuards() (3ca146d)
  • guard: Prevent RateLimit-Remaining from going below 0 (25e33c8)
  • guard: Prevent RateLimit-Remaining from going below 0 (74b1668)
  • guard: Use the correct approach to check for excluded routes (38eac3c)
  • guard: Use the correct approach to check for excluded routes (912813f)
  • req.method value in httpPromise (b9ee26e)

Features

  • Swap excludeRoutes for @SkipThrottle() decorator (16d6fac)
  • fastify: updates guard to work for fastify (bc678a3)
  • Implement ignoreRoutes functionality (7b8ab42)
  • package.json: Add --watch to start:dev script (3c4c28a)
  • Implement ignoreRoutes functionality (75f870c)
  • module: implements start of limiter module (35dbff5)
  • package.json: Add --watch to start:dev script (a6b441c)
  • Add global ThrottlerGuard (9a84aff)
  • adds httpromise for async/await http calls in tests (70210c7)
  • Rename certain variables to use the THROTTLER prefix (6a21b21)
  • Setup example app (df6b5f6)
  • Setup example app (30c7576)