#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.
#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" } } }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 bequota-inbound
.handler.export
<string>
- The name of the exported type. Value should beQuotaInboundPolicy
.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.
period
(required)<string>
- The period of the quota. Allowed values arehourly
,daily
,weekly
,monthly
.quotaBy
(required)<string>
- The quota by. Allowed values areuser
,function
. Defaults to"user"
.quotaAnchorMode
<string>
- How the policy determines the anchor date for ongoing quota cycles - defaults tofirst-api-call
which uses the first API call for this key. Allowed values arefirst-api-call
,function
. Defaults to"first-api-call"
.allowances
<object>
- The allowances for the quota.quotaOnStatusCodes
<undefined>
- 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
(required)<string>
- Specifies the module to load your custom functions, in the format$import(./modules/my-module)
. Defaults to"$import(./modules/my-module)"
.getAnchorDateExport
<string>
- used when quotaAnchorMode isfunction
. Specifies the export to load your custom function to get the anchor date. Defaults to"getAnchorDate"
.getQuotaDetailExport
<string>
- used when quotaBy isfunction
. Specifies the export to load your custom function to get the quota detail. Defaults to"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 } } } }json
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 });ts
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" } } }json
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 { GetQuotaDetailFunction } from "@zuplo/runtime"; export const getQuotaDetail: GetQuotaDetailFunction = async ( request, context, policyName, ) => { return { key: request.user.data.organizationId, allowances: { bananas: 100, }, }; };ts
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" } } }json
// ./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); };ts
#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 } }ts
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