module:middleware/limiter
- Description:
Rate Limiter Middleware
The middleware can be configured to rate by IP, path, session or user, only matched paths are checked, so limiter protection is explicit by the config, no defaults.
When using Redis for limiter it is advisable to always set ttl: to expire cache and not keep every IP or path indefinitely.
All valid parameters for
module:api.limitercan be placed in the config butrateis required.Path config and rating logic
The path part in the config behaves differently depending how wildcards are used:
*is used as is, no substitution, this means rate limiting/api/*will rate all requests like/api/1 or /api/2/3under the same token/api/*- named params in the path are replaced with actual values, so for config
/api/:id/*requests like/api/1/itemand/api/2/itemwill be rated separately as/api/1/* and /api/2/*, but/api/1/itemand/api/1/userwill be rated by the same bucket/api/1/*
Configurations with the same path and method(s) are merged into a single object, i.e. changes are accumulated, to clear values explicit values must be set like 0 or null.
Users and sessions
The difference between user and session is that sesion id is just parsed out from cookies or headers, no verification is done while rate by user requires valid user to be verified and set in the context.
By default the limiter middleware is executed before the users middleware so the
userconfig is for programmatic use mostly, seemodule:api/validatehow to rate users with validation.If session id is missing rate limiter still uses undefined instead which puts all no session requests into single bucket.
Examples:
# Rate every IP address for all /api endpoints, allow 100 req/s from each IP middleware-limiter-ip-*-/api/* = rate:100 # Merge with previous entry, add ttl middleware-limiter-ip-*-/api/* = rate:100,ttl:900000 # Rate every post for all /api/account endpoints, allow 1000 req/s globally middleware-limiter-path-post-/api/account/* = rate:1000,ttl:900000 # Rate every session id for /api/XXX/ endpoints by XXX, allow 1 req/s for each session middleware-limiter-session-post,put-/api/:type/* = rate:1 # Rate every user for /api endpoints, allow 1 req/s for each user, reorder middlewares to handle users before this one middleware-users-priority = 10 middleware-limiter-priority = 11 middleware-limiter-user-post,put-/api/* = rate:1Global mode
Enable via
middleware-limiter-enable = trueto dynamically check every request path, this allows to add more or modify routes without restartingFixed config mode
To enable just what is in the config on start and ignore new routes, modifying existing routes is still supported
middleware-limiter-enable = fixed
- Source:
Members
(static) args :Array.<ConfigOptions>
- Source:
- Default Value:
[ { "name": "enable", "descr": "Enable the middlware, 'true' means dynamicaly check all requests, 'fixed' means set routes from the config on start" }, { "name": "(ip|path|user|session)-([a-z,*]+)-(/.+)", "type": "map", "no_camel": 1, "ephemeral": 1, "onupdate": "", "descr": "Rate limit by method and path and IP/path/session/user", "example": "middleware-limiter-ip-*-/account = rate:10,interval:30000\nmiddleware-limiter-path-post-/webhook/* = rate:100,interval:30000\nmiddleware-limiter-user-get,post,put-/admin/* = rate:10" }, { "name": "reset", "type": "callback", "callback": "", "descr": "Reset all rules" }, { "name": "priority", "type": "int", "descr": "Add routes with this priority sorting number, for config mode only" } ]
Methods
(static) configureMiddleware()
- Description:
Start global middleware if enabled
- Source:
(static) handle(context, next)
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
context |
RequestContext | |
next |
function() |
Example
const { api, middleware } = require("backendjs");
const { limiter, users } = middleware;
api.app.post("*", limiter)
api.app.post("/account/*", limiter)
api.app.post("/account", { ip: { rate: 100 }, path: { rate: 200 }, handle: limiter.handle })
api.app.post("/account", users, { user: { rate: 1 }, handle: limiter.handle })
api.app.post("/account", { session: { rate: 10 }, handle: limiter.handle })