middleware/limiter

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.limiter can be placed in the config but rate is 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/3 under the same token /api/*
    • named params in the path are replaced with actual values, so for config /api/:id/* requests like /api/1/item and /api/2/item will be rated separately as /api/1/* and /api/2/*, but /api/1/item and /api/1/user will 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 user config is for programmatic use mostly, see module:api/validate how 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:1
    

    Global mode

    Enable via middleware-limiter-enable = true to dynamically check every request path, this allows to add more or modify routes without restarting

    Fixed 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 })