Policies
Quota Policy
You can use the Quota policy to limit the number of requests that are allowed to happen in a given time period (e.g., monthly). The policy can be applied the users or based on custom keys.
It supports monthly
, weekly
, daily
and hourly
quotas. By default a
requests
meter is incremented by 1 for every request but you can also quota by
other arbitrary meters; more on this below.
Beta
This policy is in beta. You can use it today, but it may change in non-backward compatible ways before the final release.
Configuration
The configuration shows how to configure the policy in the 'policies.json' document.
{ "name": "my-quota-inbound-policy", "policyType": "quota-inbound", "handler": { "export": "QuotaInboundPolicy", "module": "$import(@zuplo/runtime)", "options": { "allowances": { "requests": 10 }, "period": "monthly", "quotaBy": "user", "quotaOnStatusCodes": "200-399" } } }
Policy Options
The options for this policy are specified below. All properties are optional unless specifically marked as required.
period
<string> (Required) -The period of the quota.Allowed values arehourly
,daily
,weekly
, andmonthly
.quotaBy
<string> (Required) -The quota by.Allowed values areuser
, andfunction
. Defaults to"user"
.quotaAnchorMode
<string> -How the policy determines the anchor date for ongoing quota cycles - defaults toAllowed values arefirst-api-call
which uses the first API call for this key.first-api-call
, andfunction
. Defaults to"first-api-call"
.allowances
<object> -The allowances for the quota.quotaOnStatusCodes
<string | number[]> -A list of successful status codes and ranges "200-299, 304" that should trigger a quota increment.Defaults to"200-299"
.identifier
<object> -The module and functions to dynamically set the anchor date and/or the key/allowances for this request.module
<string> (Required) -Specifies the module to load your custom functions, in the formatDefaults to$import(./modules/my-module)
."$import(./modules/my-module)"
.getAnchorDateExport
<string> -used when quotaAnchorMode isDefaults tofunction
. Specifies the export to load your custom function to get the anchor date."getAnchorDate"
.getQuotaDetailExport
<string> -used when quotaBy isDefaults tofunction
. Specifies the export to load your custom function to get the quota detail."getQuotaDetail"
.
Using the Policy
The Quota policy needs to know when to anchor the quota start date so that the
Zuplo runtime can know where in the quota cycle you are. By default the runtime
uses the "quotaAnchorMode": "first-api-call"
which checks to see if we have an
existing quota record for this user or custom quota key and, if not, sets it
based on the time of the first API call for this key.
You can customize the subscription date by setting the getAnchorDateExport
function, more below under Custom Anchor Date.
Quota Cycles / Periods#
The quota periods run from the anchor date and time until the next matching
cycle. For monthly
periods, if the anchor date is 2024-01-31 04:30Z
then the
quota cycle will terminate on the same day of the next month or the last day of
that month if it is a shorter month, at the same time. In this case the quota
cycle will reset on 2024-02-29 04:30Z
(because 2024 is a leap year).
weekly
cycles shift on the same day of the next week, at the same time.
daily
on the next day, at the same time.
hourly
on the same minute, of the next hour.
Custom Meters#
You can set custom meters in the allowances property of the options to include
custom meters other than requests
. For example, here we set a monthly
allowance of 10 bananas
.
{ "name": "my-quota-inbound-policy", "policyType": "quota-inbound", "handler": { "export": "QuotaInboundPolicy", "module": "$import(@zuplo/runtime)", "options": { "period": "monthly", "quotaBy": "user", "allowances": { "bananas": 10 } } } }
For this to work you must tell the runtime how many bananas
to increment in a
given request/response lifecycle. This is achieved by using the setMeters
method on the QuotaInboundPolicy
class:
import { QuotaInboundPolicy } from "@zuplo/runtime"; // ... QuotaInboundPolicy.setMeters(context, { bananas: 5, oranges: 3 });
This is typically invoked in a custom inbound or outbound policy or a handler.s
Dynamic Quota Allowances and Keys#
Like Dynamic Rate Limiting, Quota Keys and allowances can also be set
dynamically in Zuplo. This is achieved by setting the identifier
module and
getQuotaDetailExport
in your options:
{ "name": "my-quota-inbound-policy", "policyType": "quota-inbound", "handler": { "export": "QuotaInboundPolicy", "module": "$import(@zuplo/runtime)", "options": { "identifier": { "getQuotaDetailExport": "getQuotaDetail", "module": "$import(./modules/my-module)" }, "quotaAnchorMode": "first-api-call", "quotaBy": "user" } } }
If you wanted to key on a property from the user metadata, like organizationId
you might have a getQuotaDetail
implementation that looks like this.
// ./modules/my-module.ts import { GetQuotaAnchorDateFunction } from "@zuplo/runtime"; export const getQuotaDetail: GetQuotaDetailFunction = async ( request, context, policyName ) => { return { key: request.user.data.organizationId, allowances: { bananas: 100, }, }; };
Note that this method supports async calls and could be used to load quotas from another API, but we would recommend caching the results for performance reasons.
Custom Anchor Date#
Similarly, the Anchor Date can be set programmatically - for example you may load the 'subscription' start date from another database or API.
{ "name": "my-quota-inbound-policy", "policyType": "quota-inbound", "handler": { "export": "QuotaInboundPolicy", "module": "$import(@zuplo/runtime)", "options": { "identifier": { "getAnchorDateExport": "getQuotaDetail", "module": "$import(./modules/my-module)" }, "quotaAnchorMode": "first-api-call", "quotaBy": "user" } } }
// ./modules/my-module.ts import { GetQuotaAnchorDateFunction } from "@zuplo/runtime"; export const getAnchorDate: GetQuotaAnchorDateFunction = async ( request, context, policyName ) => { // simple example fetch call, needs error handling, auth etc. const response = await fetch( `https://my-subs-api/subs/${request.user.data.organizationId}` ); const data = await response.json(); return new Date(data.startDate); };
Get Usage#
You can also programmatically access the usage counts with the getUsage
static
function on QuotaInboundPolicy
. This call must occur after the
Quota-Inbound policy has executed.
const usage = QuotaInboundPolicy.getUsage(context, 'quota-policy-name'); context.log.info(usage); // This would generate the following output: { anchorDate: string; nextResetDate: string; meters: Record<string, number>; } // example { anchorDate: "2023-08-20T03:05:05.493Z", nextResetDate: "2024-08-20T03:05:05.493Z", meters: { requests: 1, bananas: 10 } }
Note that if the quota has not yet been updated for a particular meter, the meter will be undefined in the response.
Read more about how policies work