Security & Validation

#Rate Limiting Policy

Rate-limiting allows you to set a maximum rate of requests for your API gateway. This is useful to enforce rate limits agreed with your clients and protect your downstream services.

The Zuplo Rate-Limit allows you to limit based on different attributes of the incoming request. For example, you might set a rate limit of 10 requests per second per user, or 20 requests per second for a given IP address.

The Zuplo rate-limiter also allows you to set a custom bucket name by which to effect a rate-limit using a function.

When a client reaches a rate limit - they will receive a 429 response code.

#Configuration

The configuration shows how to configure the policy in the 'policies.json' document.

{
  "name": "my-rate-limit-inbound-policy",
  "policyType": "rate-limit-inbound",
  "handler": {
    "export": "RateLimitInboundPolicy",
    "module": "$import(@zuplo/runtime)",
    "options": {
      "rateLimitBy": "ip",
      "requestsAllowed": 2,
      "timeWindowMinutes": 1
    }
  }
}
json

#Policy Configuration

  • name <string> - The name of your policy instance. This is used as a reference in your routes.
  • policyType <string> - The identifier of the policy. This is used by the Zuplo UI. Value should be rate-limit-inbound.
  • handler.export <string> - The name of the exported type. Value should be RateLimitInboundPolicy.
  • handler.module <string> - The module containing the policy. Value should be $import(@zuplo/runtime).
  • handler.options <object> - The options for this policy. See Policy Options below.

#Policy Options

The options for this policy are specified below. All properties are optional unless specifically marked as required.

  • rateLimitBy (required) <string> - The identifying element of the request that enforces distinct rate limits. For example, you can limit by user, ip, function or all - function allows you to specify a simple function to create a string identifier to create a rate-limit group. Allowed values are user, ip, function, all. Defaults to "user".
  • requestsAllowed (required) <integer> - The max number of requests allowed in the given time window. Defaults to 1000.
  • timeWindowMinutes (required) <integer> - The time window in which the requests are rate-limited. The count restarts after each window expires. Defaults to 60.
  • identifier <object> - The function that returns dynamic configuration data. Used only with rateLimitBy=function.
    • export (required) <string> - used only with rateLimitBy=function. Specifies the export to load your custom bucket function, e.g. default, rateLimitIdentifier. Defaults to "$import(./modules/my-module)".
    • module (required) <string> - Specifies the module to load your custom bucket function, in the format $import(./modules/my-module). Defaults to "".
  • headerMode <string> - Adds the retry-after header. Allowed values are none, retry-after. Defaults to "retry-after".
  • throwOnFailure <boolean> - If true, the policy will throw an error in the event there is a problem connecting to the rate limit service. Defaults to false.
  • mode <string> - The mode of the policy. If set to async, the policy will check if the request is over the rate limit without blocking. This can result in some requests allowed over the rate limit. Allowed values are strict, async. Defaults to "strict".

#Using the Policy

Note you can have multiple instances of rate-limiting policies to use in combination. You should apply the longest duration timeWindow first, in order to the shortest duration time window.

#Using a custom function

You can create a rate-limit bucket based on any property of a request using a custom function that returns a CustomRateLimitDetails object (which provides the identifier used by the limiting system).

The CustomRateLimitDetails object can be used to override the timeWindowMinutes & requestsAllowed options.

This example would create a unique rate-limiting function based on the customerId parameter in routes (note it’s important that a policy like this is applied to a route that has a /:customerId parameter).

//module - ./modules/rate-limiter.ts

import {
  CustomRateLimitDetails,
  ZuploRequest,
  ZuploContext,
} from "@zuplo/runtime";

export function rateLimitKey(
  request: ZuploRequest,
  context: ZuploContext,
  policyName: string,
): CustomRateLimitDetails | undefined {
  context.log.info(
    `processing customerId '${request.params.customerId}' for rate-limit policy '${policyName}'`,
  );
  if (request.params.customerId === "43567890") {
    // Override timeWindowMinutes & requestsAllowed
    return {
      key: request.params.customerId,
      requestsAllowed: 100,
      timeWindowMinutes: 1,
    };
  }
}
ts
// config - ./config/policies.json
"export": "RateLimitInboundPolicy",
"module": "$import(@zuplo/runtime)",
"options": {
  "rateLimitBy": "function",
  "requestsAllowed": 2,
  "timeWindowMinutes": 1,
  "identifier": {
    "module": "$import(./modules/rate-limiter)",
    "export": "rateLimitKey"
  }
}
json

Read more about how policies work