A slow down plugin for fastify
npm i fastify-slow-down
Register SlowDown as a Fastify plugin.
This plugin will add an onRequest
hook to slow down replies if a client (based on their IP address by default) has made too many multiple requests in the given timeWindow
and
it will add slowDown
request decorator, which is an object with the following properties:
Name | Type | Description |
---|---|---|
limit | number | the maximum limit until the server starts to delay the response |
current | number | the index of the current request |
remaining | number | how many requests are left until the server starts to delay the response |
delay | number | undefined | value of the delay (in milliseconds) applied to this request |
import Fastify from 'fastify'
import slowDownPlugin from 'fastify-slow-down'
const fastify = Fastify()
// register the plugin
fastify.register(slowDownPlugin)
// create a route
fastify.get('/', async () => 'Hello from fastify-slow-down!')
// start server
await fastify.listen({ port: 3000 })
The response will have some additional headers:
Header | Description |
---|---|
x-slow-down-limit |
how many requests in total the client can make until the server starts to slow down the response |
x-slow-down-remaining |
how many requests remain to the client in the timeWindow |
x-slow-down-delay |
how much delay (in milliseconds) has been applied to this request |
Name | Type | Default Value | Description |
---|---|---|---|
delay | string | number | 1 second | Base unit of time delay applied to requests. It can be expressed in milliseconds or as string in ms format. Set to 0 to disable delaying. |
delayAfter | number | 5 | number of requests received during timeWindow before starting to delay responses. Set to 0 to disable delaying. |
maxDelay | string, number | Infinity | the maximum value of delay that a request has after many consecutive attempts. It is an important option for the server when it is running behind a load balancer or reverse proxy, and has a request timeout. Set to 0 to disable delaying. |
timeWindow | string |, number | 30 seconds | The duration of the time window during which request counts are kept in memory. It can be expressed in milliseconds or as a string in ms format. Set to 0 to disable delaying. |
inMemoryCacheSize | number | 5000 | The maximum number of items that will be stored in the in-memory cache (this plugin internally uses a lru cache to handle the clients, you can change the size of the cache with this option) |
redis | Redis client instance from the ioredis package |
undefined | by default this plugin uses an in-memory store, but if your application works on more than one server it is useless, since the data is stored locally. You can pass a Redis client here and magically the issue is solved. To achieve the maximum speed, this plugin requires the use of ioredis . Note: the default parameters of a redis connection are not the fastest to provide a slow-down. We suggest to customize the connectTimeout and maxRetriesPerRequest as in the example. |
headers | boolean | true | flag to add custom headers x-slow-down-limit , x-slow-down-remaining , x-slow-down-delay for all server responses. |
keyGenerator | function | (req) => req.ip | Function used to generate keys to uniquely identify requests coming from the same user |
onLimitReached | function | undefined | Function that gets called the first time the limit is reached within timeWindow . |
skipFailedRequests | boolean | false | When true , failed requests (status >= 400) won't be counted. |
skipSuccessfulRequests | boolean | false | When true , successful requests (status < 400) won't be counted. |
skip | function | undefined | Function used to skip requests. Returning true from the function will skip limiting for that request. |
Registering the plugin with these options:
fastify.register(slowDownPlugin, {
delay: '10 seconds',
delayAfter: 10,
timeWindow: '10 minutes',
maxDelay: '100 seconds'
})
A delay specified via the delay
option will be applied to requests coming from the same IP address (by default) when more than delayAfter
requests are received within the time specified in the timeWindow
option.
In 10 minutes the result of hitting the API will be:
- 1st request - no delay
- 2nd request - no delay
- 3rd request - no delay
...
- 10th request - no delay
- 11th request - 10 seconds delay
- 12th request - 20 seconds delay
- 13th request - 30 seconds delay
...
- 20th request - 100 seconds delay
- 21st request - 100 seconds delay*
After 10 minutes without hitting the API the results will be:
- 21th request - no delay
- 21th request - no delay
...
- 30th request - no delay
- 31th request - 10 seconds delay
*Delay remains the same because the value of maxDelay
option is 100 seconds
Integration with fastify-rate-limit
The plugin slows down the response without stopping it at a certain limit. This can put the server in a difficult situation because the client can make many requests without waiting for a response. To limit the number of requests, it should be used together with the fastify-rate-limit
plugin.
See in this example which is the implementation using fastify-slow-down
and fastify-rate-limit
plugins.
If the delayAfter
value of the fastify-slow-down
package is set to be less than the max
value of the fastify-rate-limit
package, then you will see the responses being delayed once the delayAfter
value has been exceeded, by the number of ms
as specified in the delay
value.
However, once the max
value from the fastify-rate-limit
package has been exceeded, then as expected you will still have rate limit errors returned but they will also be delayed according to the delay
value from the fastify-slow-down
package assuming that the delayAfter
value has also been exceeded.