rateLimit
Rate limits service method calls using rate-limiter-flexible. You provide a pre-configured RateLimiterAbstract instance (Memory, Redis, Mongo, etc.) and the hook consumes points per request.
import { rateLimit } from 'feathers-utils/hooks';Example
import { rateLimit } from 'feathers-utils/hooks'
import { RateLimiterMemory } from 'rate-limiter-flexible'
const rateLimiter = new RateLimiterMemory({ points: 10, duration: 1 })
app.service('users').hooks({
before: { find: [rateLimit(rateLimiter)] }
})Type declaration
Show Type Declarations
export type RateLimitOptions<H extends HookContext = HookContext> = {
/**
* The rate-limiting key, or a function to derive it from the context.
*
* Pass a static string to use a single shared bucket (a global rate limit
* across all requests), or a function to compute the key per request
* (e.g. per user or per IP).
*
* @default context.path
*/
key?: string | ((context: H) => Promisable<string>)
/**
* Number of points to consume per request, or a function to compute it from
* the context.
*
* @default 1
*/
points?: number | ((context: H) => Promisable<number>)
}
/**
* Rate limits service method calls using `rate-limiter-flexible`.
* You provide a pre-configured `RateLimiterAbstract` instance
* (Memory, Redis, Mongo, etc.) and the hook consumes points per request.
*
* @example
* ```ts
*
*
*
* const rateLimiter = new RateLimiterMemory({ points: 10, duration: 1 })
*
* app.service('users').hooks({
* before: { find: [rateLimit(rateLimiter)] }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/rate-limit.html
*/
export declare const rateLimit: <H extends HookContext = HookContext>(
rateLimiter: RateLimiterAbstract,
options?: RateLimitOptions<H>,
) => (context: H, next?: NextFunction) => Promise<void>| Argument | Type | Description |
|---|---|---|
| rateLimiter | RateLimiterAbstract | |
| options | RateLimitOptions<H> |
| type | methods | multi |
|---|---|---|
| before, around | find, get, create, update, patch, remove | yes |
The rateLimit hook limits how many times a service method can be called within a time window using rate-limiter-flexible. You provide a pre-configured rate limiter instance — the hook calls consume() on each request and throws a TooManyRequests error when the limit is exceeded.
Any rate limiter backend supported by rate-limiter-flexible can be used (Memory, Redis, Mongo, Postgres, etc.).
Options
The RateLimiterRes is stored on context.params.rateLimit on both success and failure, so downstream hooks or services can inspect remainingPoints, consumedPoints, msBeforeNext, etc.
Examples
Basic Usage
import { rateLimit } from 'feathers-utils/hooks'
import { RateLimiterMemory } from 'rate-limiter-flexible'
const rateLimiter = new RateLimiterMemory({
points: 10, // 10 requests
duration: 1, // per 1 second
})
app.service('users').hooks({
before: {
find: [rateLimit(rateLimiter)],
},
})Rate Limit per User
Use the key option to rate limit per authenticated user instead of per service path:
const rateLimiter = new RateLimiterMemory({ points: 100, duration: 60 })
app.service('messages').hooks({
before: {
create: [
rateLimit(rateLimiter, {
key: (context) => `${context.path}:${context.params.user?.id}`,
}),
],
},
})Global Rate Limit
Pass a static string as the key to share a single bucket across all requests — a global cap on an endpoint instead of one bucket per context.path:
const rateLimiter = new RateLimiterMemory({ points: 1000, duration: 60 })
app.service('search').hooks({
before: {
find: [rateLimit(rateLimiter, { key: 'search' })],
},
})Custom Points per Request
Pass a static number to consume a fixed cost per request:
app.service('reports').hooks({
before: {
find: [rateLimit(rateLimiter, { points: 5 })],
},
})Or pass a function to compute the cost from the context — e.g. to charge more for expensive queries:
app.service('reports').hooks({
before: {
find: [
rateLimit(rateLimiter, {
points: (context) => (context.params.query?.$limit > 100 ? 5 : 1),
}),
],
},
})Redis Backend
import { RateLimiterRedis } from 'rate-limiter-flexible'
import Redis from 'ioredis'
const redisClient = new Redis()
const rateLimiter = new RateLimiterRedis({
storeClient: redisClient,
points: 100,
duration: 60,
keyPrefix: 'rl',
})
app.service('users').hooks({
before: {
find: [rateLimit(rateLimiter)],
},
})Bypass with iff
Use iff to skip rate limiting for internal (server-side) calls:
import { rateLimit, iff } from 'feathers-utils/hooks'
import { isProvider } from 'feathers-utils/predicates'
app.service('users').hooks({
before: {
find: [
iff(isProvider('rest', 'socketio', 'external'), rateLimit(rateLimiter)),
],
},
})Bypass with skippable
Use skippable to allow specific callers to opt out of rate limiting:
import { rateLimit, skippable } from 'feathers-utils/hooks'
app.service('users').hooks({
before: {
find: [skippable(rateLimit(rateLimiter))],
},
})
// Skip rate limiting for this call
app.service('users').find({ skipHooks: ['rateLimit'] })