@commandkit/ratelimit is the official CommandKit plugin for advanced rate limiting. It provides multi-window policies, role overrides, queueing, exemptions, and multiple algorithms while keeping command handlers lean.
The ratelimit() factory returns two plugins in order: the compiler plugin for the "use ratelimit" directive and the runtime plugin that enforces limits. Runtime options must be configured before the runtime plugin activates.
- Installation
- Setup
- Runtime configuration lifecycle
- Basic usage
- Configuration reference
- Limiter resolution and role strategy
- Scopes and keying
- Algorithms
- Storage
- Queue mode
- Violations and escalation
- Bypass and exemptions
- Responses, hooks, and events
- Resets and HMR
- Directive:
use ratelimit - Defaults and edge cases
- Duration parsing
- Exports
Install the ratelimit plugin to get started:
npm install @commandkit/ratelimitAdd the ratelimit plugin to your CommandKit configuration and define a runtime config file.
Create an auto-loaded runtime config file (for example ratelimit.ts) and configure the default limiter:
// ratelimit.ts
import { configureRatelimit } from '@commandkit/ratelimit';
configureRatelimit({
defaultLimiter: {
maxRequests: 5,
interval: '1m',
scope: 'user',
algorithm: 'fixed-window',
},
});Register the plugin in your config:
// commandkit.config.ts
import { defineConfig } from 'commandkit';
import { ratelimit } from '@commandkit/ratelimit';
export default defineConfig({
plugins: [ratelimit()],
});The runtime plugin auto-loads ratelimit.ts or ratelimit.js on startup before commands execute.
graph TD
A[App startup] --> B[Auto-load ratelimit.ts/js]
B --> C["configureRatelimit()"]
C --> D["Runtime plugin activate()"]
D --> E[Resolve storage]
E --> F[Resolve limiter config]
F --> G[Consume algorithm]
G --> H[Aggregate result]
H --> I[Default response / hooks / events]
RateLimitPlugin.activate() throws if configureRatelimit() was not called. This is enforced to avoid silently running without your intended defaults.
configureRatelimit() merges your config into an in-memory object and sets the configured flag. getRateLimitConfig() returns the current object, and isRateLimitConfigured() returns whether initialization has happened. If a runtime context is already active, configureRatelimit() updates it immediately.
Storage is resolved in this order:
| Order | Source | Notes |
|---|---|---|
| 1 | Limiter storage override |
RateLimitLimiterConfig.storage for the command being executed. |
| 2 | Plugin storage option |
RateLimitPluginOptions.storage. |
| 3 | Process default | Set via setRateLimitStorage() or setDriver(). |
| 4 | Default memory storage | Used unless initializeDefaultStorage or initializeDefaultDriver is false. |
If no storage is resolved and defaults are disabled, the plugin logs once and stores an empty result without limiting.
These helpers are process-wide:
| Helper | Purpose |
|---|---|
configureRatelimit |
Set runtime options and update active runtime state. |
getRateLimitConfig |
Read the merged in-memory runtime config. |
isRateLimitConfigured |
Check whether configureRatelimit() was called. |
setRateLimitStorage |
Set the default storage for the process. |
getRateLimitStorage |
Get the process default storage (or null). |
setDriver / getDriver |
Aliases for setRateLimitStorage / getRateLimitStorage. |
setRateLimitRuntime |
Set the active runtime context for APIs and directives. |
getRateLimitRuntime |
Get the active runtime context (or null). |
Use command metadata or the use ratelimit directive to enable rate limiting.
This section focuses on command metadata; see the directive section for
function-level usage.
Enable rate limiting by setting metadata.ratelimit:
export const metadata = {
ratelimit: {
maxRequests: 3,
interval: '10s',
scope: 'user',
algorithm: 'sliding-window',
},
};metadata.ratelimit can be one of:
| Value | Meaning |
|---|---|
false or undefined |
Plugin does nothing for this command. |
true |
Enable rate limiting using resolved defaults. |
RateLimitCommandConfig |
Enable rate limiting with command-level overrides. |
If env.context is missing in the execution environment, the plugin skips rate limiting.
configureRatelimit({
limiters: {
heavy: { maxRequests: 1, interval: '10s', algorithm: 'fixed-window' },
},
});export const metadata = {
ratelimit: {
limiter: 'heavy',
scope: 'user',
},
};| Field | Type | Default or resolution | Notes |
|---|---|---|---|
defaultLimiter |
RateLimitLimiterConfig |
DEFAULT_LIMITER when unset |
Base limiter for all commands and directives. |
limiters |
Record<string, RateLimitLimiterConfig> |
undefined |
Named limiter presets. |
storage |
RateLimitStorageConfig |
undefined |
Resolved before default storage. |
keyPrefix |
string |
undefined |
Prepended before rl:. |
keyResolver |
RateLimitKeyResolver |
undefined |
Used for custom scope when the limiter does not override it. |
bypass |
RateLimitBypassOptions |
undefined |
Permanent allowlists and optional check. |
hooks |
RateLimitHooks |
undefined |
Lifecycle callbacks. |
onRateLimited |
RateLimitResponseHandler |
undefined |
Overrides default reply. |
queue |
RateLimitQueueOptions |
undefined |
If any queue config exists, enabled defaults to true. |
roleLimits |
Record<string, RateLimitLimiterConfig> |
undefined |
Base role limits. |
roleLimitStrategy |
RateLimitRoleLimitStrategy |
highest when resolving |
Used when multiple roles match. |
initializeDefaultStorage |
boolean |
true |
Disable to prevent memory fallback. |
initializeDefaultDriver |
boolean |
true |
Alias for initializeDefaultStorage. |
| Field | Type | Default or resolution | Notes |
|---|---|---|---|
maxRequests |
number |
10 when missing or <= 0 |
Used by fixed and sliding windows. |
interval |
DurationLike |
60s when missing or invalid |
Parsed and clamped to >= 1ms. |
scope |
RateLimitScope or RateLimitScope[] |
user |
Arrays are deduplicated. |
algorithm |
RateLimitAlgorithmType |
fixed-window |
Unknown values fall back to fixed-window. |
burst |
number |
maxRequests when missing or <= 0 |
Capacity for token or leaky buckets. |
refillRate |
number |
maxRequests / intervalSeconds |
Must be > 0 for token bucket. |
leakRate |
number |
maxRequests / intervalSeconds |
Must be > 0 for leaky bucket. |
keyResolver |
RateLimitKeyResolver |
undefined |
Used only for custom scope. |
keyPrefix |
string |
undefined |
Overrides plugin prefix for this limiter. |
storage |
RateLimitStorageConfig |
undefined |
Overrides storage for this limiter. |
violations |
ViolationOptions |
undefined |
Enables escalation unless escalate is false. |
queue |
RateLimitQueueOptions |
undefined |
Overrides queue settings at this layer. |
windows |
RateLimitWindowConfig[] |
undefined |
Enables multi-window behavior. |
roleLimits |
Record<string, RateLimitLimiterConfig> |
undefined |
Role overrides at this layer. |
roleLimitStrategy |
RateLimitRoleLimitStrategy |
highest when resolving |
Used when role limits match. |
| Field | Type | Default or resolution | Notes |
|---|---|---|---|
id |
string |
w1, w2, ... |
Auto-generated if empty or missing. |
maxRequests |
number |
Inherits from base limiter | Applies only to this window. |
interval |
DurationLike |
Inherits from base limiter | Parsed like the base limiter. |
algorithm |
RateLimitAlgorithmType |
Inherits from base limiter | Usually keep consistent across windows. |
burst |
number |
Inherits from base limiter | Used for token or leaky buckets. |
refillRate |
number |
Inherits from base limiter | Must be > 0 for token bucket. |
leakRate |
number |
Inherits from base limiter | Must be > 0 for leaky bucket. |
violations |
ViolationOptions |
Inherits from base limiter | Overrides escalation for this window. |
| Field | Type | Default or resolution | Notes |
|---|---|---|---|
enabled |
boolean |
true when any queue config exists |
Otherwise false. |
maxSize |
number |
3 and clamped to >= 1 |
Queue size is pending plus running. |
timeout |
DurationLike |
30s and clamped to >= 1ms |
Per queued task. |
deferInteraction |
boolean |
true unless explicitly false |
Only used for interactions. |
ephemeral |
boolean |
true unless explicitly false |
Applies to deferred replies. |
concurrency |
number |
1 and clamped to >= 1 |
Per queue key. |
| Field | Type | Default or resolution | Notes |
|---|---|---|---|
escalate |
boolean |
true when violations is set |
Set false to disable escalation. |
maxViolations |
number |
5 |
Maximum escalation steps. |
escalationMultiplier |
number |
2 |
Multiplies cooldown per repeated violation. |
resetAfter |
DurationLike |
1h |
TTL for violation state. |
RateLimitCommandConfig extends RateLimitLimiterConfig and adds:
| Field | Type | Default or resolution | Notes |
|---|---|---|---|
limiter |
string |
undefined |
References a named limiter in limiters. |
RateLimitStoreValue:
| Field | Type | Meaning |
|---|---|---|
limited |
boolean |
true if any scope or window was limited. |
remaining |
number |
Minimum remaining across all results. |
resetAt |
number |
Latest reset timestamp across all results. |
retryAfter |
number |
Max retry delay across limited results. |
results |
RateLimitResult[] |
Individual results per scope and window. |
RateLimitResult:
| Field | Type | Meaning |
|---|---|---|
key |
string |
Storage key used for the limiter. |
scope |
RateLimitScope |
Scope applied for the limiter. |
algorithm |
RateLimitAlgorithmType |
Algorithm used for the limiter. |
windowId |
string |
Present for multi-window limits. |
limited |
boolean |
Whether this limiter hit its limit. |
remaining |
number |
Remaining requests or capacity. |
resetAt |
number |
Absolute reset timestamp in ms. |
retryAfter |
number |
Delay until retry is allowed, in ms. |
limit |
number |
maxRequests for fixed and sliding, burst for token and leaky buckets. |
Limiter configuration is layered in this exact order, with later layers overriding earlier ones:
| Order | Source | Notes |
|---|---|---|
| 1 | DEFAULT_LIMITER |
Base defaults. |
| 2 | defaultLimiter |
Runtime defaults. |
| 3 | Named limiter | When metadata.ratelimit.limiter is set. |
| 4 | Command overrides | metadata.ratelimit config. |
| 5 | Role override | Selected by role strategy. |
graph TD
A[DEFAULT_LIMITER] --> B[defaultLimiter]
B --> C[Named limiter]
C --> D[Command overrides]
D --> E["Role override (strategy)"]
Role limits are merged in this order, with later maps overriding earlier ones for the same role id:
| Order | Source |
|---|---|
| 1 | Plugin roleLimits |
| 2 | defaultLimiter.roleLimits |
| 3 | Named limiter roleLimits |
| 4 | Command roleLimits |
Role strategies:
| Strategy | Selection rule |
|---|---|
highest |
Picks the role with the highest request rate (maxRequests / intervalMs). |
lowest |
Picks the role with the lowest request rate. |
first |
Uses insertion order of the merged role limits object. |
For multi-window limiters, the score uses the minimum rate across windows.
Supported scopes:
| Scope | Required IDs | Key format (without keyPrefix) |
Skip behavior |
|---|---|---|---|
user |
userId |
rl:user:{userId}:{commandName} |
Skips if userId is missing. |
guild |
guildId |
rl:guild:{guildId}:{commandName} |
Skips if guildId is missing. |
channel |
channelId |
rl:channel:{channelId}:{commandName} |
Skips if channelId is missing. |
global |
none | rl:global:{commandName} |
Never skipped. |
user-guild |
userId, guildId |
rl:user:{userId}:guild:{guildId}:{commandName} |
Skips if either id is missing. |
custom |
keyResolver |
keyResolver(ctx, command, source) |
Skips if resolver is missing or returns falsy. |
Keying notes:
DEFAULT_KEY_PREFIXis always included in the base format.keyPrefixis concatenated beforerl:as-is, so include a trailing separator if you want one.- Multi-window limits append
:w:{windowId}.
Temporary exemptions are stored under rl:exempt:{scope}:{id} (plus optional keyPrefix).
| Exemption scope | Key format | Notes |
|---|---|---|
user |
rl:exempt:user:{userId} |
Resolved from the source user id. |
guild |
rl:exempt:guild:{guildId} |
Resolved from the guild id. |
role |
rl:exempt:role:{roleId} |
Resolved from all member roles. |
channel |
rl:exempt:channel:{channelId} |
Resolved from the channel id. |
category |
rl:exempt:category:{categoryId} |
Resolved from the parent category id. |
| Algorithm | Required config | Storage requirements | limit value |
Notes |
|---|---|---|---|---|
fixed-window |
maxRequests, interval |
consumeFixedWindow or incr or get and set |
maxRequests |
Fallback uses per-process lock and optimistic versioning. |
sliding-window |
maxRequests, interval |
consumeSlidingWindowLog or zRemRangeByScore + zCard + zAdd |
maxRequests |
Throws if sorted-set support is missing. |
token-bucket |
burst, refillRate |
get and set |
burst |
Throws if refillRate <= 0. |
leaky-bucket |
burst, leakRate |
get and set |
burst |
Throws if leakRate <= 0. |
Execution path:
- If
consumeFixedWindowexists, it is used. - Else if
increxists, it is used. - Else a fallback uses
getandsetwith a per-process lock.
The limiter is considered limited when count > maxRequests. The fallback path retries up to five times with optimistic versioning and is serialized only within the current process.
graph TD
A[Consume fixed-window] --> B{consumeFixedWindow?}
B -- Yes --> C[Use consumeFixedWindow]
B -- No --> D{incr?}
D -- Yes --> E[Use incr]
D -- No --> F["get + set fallback (per-process lock)"]
Execution path:
- If
consumeSlidingWindowLogexists, it is used (atomic). - Else a sorted-set fallback uses
zRemRangeByScore,zCard, andzAdd.
If sorted-set methods are missing, the algorithm throws. If zRangeByScore is available, it is used to compute an accurate oldest timestamp for resetAt; otherwise resetAt defaults to now + window. The fallback is serialized per process but is not atomic across processes.
graph TD
A[Consume sliding-window] --> B{consumeSlidingWindowLog?}
B -- Yes --> C[Use consumeSlidingWindowLog]
B -- No --> D{zset methods?}
D -- No --> E[Throw error]
D -- Yes --> F[zRemRangeByScore + zCard + zAdd fallback]
Token bucket uses a stored tokens and lastRefill state. On each consume, tokens refill based on elapsed time and refillRate. If the bucket has fewer than one token, the request is limited and retryAfter is computed from the time required to refill one token.
Leaky bucket uses a stored level and lastLeak state. Each request adds one token, and the bucket drains at leakRate. If adding would exceed capacity, the request is limited and retryAfter is computed from the time required to drain the overflow.
Use windows to enforce multiple windows simultaneously:
configureRatelimit({
defaultLimiter: {
scope: 'user',
algorithm: 'sliding-window',
windows: [
{ id: 'short', maxRequests: 10, interval: '1m' },
{ id: 'long', maxRequests: 1000, interval: '1d' },
],
},
});If a window id is omitted, the plugin generates w1, w2, and so on. Window ids are part of the storage key and appear in results.
Required methods:
| Method | Used by | Notes |
|---|---|---|
get |
All algorithms | Returns stored value or null. |
set |
All algorithms | Optional ttlMs controls expiry. |
delete |
Resets and algorithm resets | Removes stored state. |
Optional methods and features:
| Method | Feature | Notes |
|---|---|---|
consumeFixedWindow |
Fixed-window atomic consume | Used before incr and fallback. |
incr |
Fixed-window efficiency | Returns count and TTL. |
consumeSlidingWindowLog |
Sliding-window atomic consume | Preferred over sorted-set fallback. |
zAdd / zRemRangeByScore / zCard |
Sliding-window fallback | Required when consumeSlidingWindowLog is absent. |
zRangeByScore |
Sliding-window reset accuracy | Improves resetAt computation. |
ttl |
Exemption listing | Used for expiresInMs. |
expire |
Sliding-window fallback | Keeps sorted-set keys from growing indefinitely. |
deleteByPrefix / deleteByPattern |
Resets | Required by resetAllRateLimits and HMR. |
keysByPrefix |
Exemption listing | Required for listing without a specific id. |
| Feature | Requires | Memory | Redis | Fallback |
|---|---|---|---|---|
| Fixed-window atomic consume | consumeFixedWindow |
Yes | Yes | Conditional (both storages) |
Fixed-window incr |
incr |
Yes | Yes | Conditional (both storages) |
| Sliding-window atomic consume | consumeSlidingWindowLog |
Yes | Yes | Conditional (both storages) |
| Sliding-window fallback | zAdd + zRemRangeByScore + zCard |
Yes | Yes | Conditional (both storages) |
| TTL visibility | ttl |
Yes | Yes | Conditional (both storages) |
| Prefix or pattern deletes | deleteByPrefix or deleteByPattern |
Yes | Yes | Conditional (both storages) |
| Exemption listing | keysByPrefix |
Yes | Yes | Conditional (both storages) |
graph TD
A[Storage API] --> B[Required: get / set / delete]
A --> C[Optional methods]
C --> D[Fixed window atomic: consumeFixedWindow / incr]
C --> E[Sliding window atomic: consumeSlidingWindowLog]
C --> F[Sliding window fallback: zAdd + zRemRangeByScore + zCard]
C --> G[Listing & TTL: keysByPrefix / ttl]
C --> H[Bulk reset: deleteByPrefix / deleteByPattern]
I[Fallback storage] --> J[Uses primary + secondary]
J --> K[Each optional method must exist on both]
import { MemoryRateLimitStorage, setRateLimitStorage } from '@commandkit/ratelimit';
setRateLimitStorage(new MemoryRateLimitStorage());Notes:
- In-memory only; not safe for multi-process deployments.
- Implements TTL and sorted-set helpers.
deleteByPatternsupports a simple*wildcard, not full glob syntax.
import { RedisRateLimitStorage } from '@commandkit/ratelimit/redis';
import { setRateLimitStorage } from '@commandkit/ratelimit';
setRateLimitStorage(
new RedisRateLimitStorage({ host: 'localhost', port: 6379 }),
);Notes:
- Stores values as JSON.
- Uses Lua scripts for atomic fixed and sliding windows.
- Uses
SCANfor prefix and pattern deletes and listing.
import { FallbackRateLimitStorage } from '@commandkit/ratelimit/fallback';
import { MemoryRateLimitStorage } from '@commandkit/ratelimit/memory';
import { RedisRateLimitStorage } from '@commandkit/ratelimit/redis';
import { setRateLimitStorage } from '@commandkit/ratelimit';
const primary = new RedisRateLimitStorage({ host: 'localhost', port: 6379 });
const secondary = new MemoryRateLimitStorage();
setRateLimitStorage(new FallbackRateLimitStorage(primary, secondary));Notes:
- Every optional method must exist on both storages or the fallback wrapper throws.
- Primary errors are logged at most once per
cooldownMswindow (default 30s).
Queue mode retries commands instead of rejecting immediately.
| Field | Default | Clamp | Notes |
|---|---|---|---|
enabled |
true if any queue config exists |
n/a | Otherwise false. |
maxSize |
3 |
>= 1 |
Queue size is pending plus running. |
timeout |
30s |
>= 1ms |
Per queued task. |
deferInteraction |
true |
n/a | Only applies to interactions. |
ephemeral |
true |
n/a | Applies to deferred replies. |
concurrency |
1 |
>= 1 |
Per queue key. |
- Rate limit is evaluated and an aggregate result is computed.
- If limited and queueing is enabled, the plugin tries to enqueue.
- If the queue is full, it falls back to immediate rate-limit handling.
- When queued, the interaction is deferred if it is repliable and not already replied or deferred.
- The queued task waits
retryAfter, then re-checks the limiter; if still limited it waits at least 250ms and retries until timeout.
graph TD
A[Evaluate limiter] --> B{Limited?}
B -- No --> C[Allow command]
B -- Yes --> D{Queue enabled?}
D -- No --> E[Rate-limit response]
D -- Yes --> F{Queue has capacity?}
F -- No --> E
F -- Yes --> G[Enqueue + defer if repliable]
G --> H[Wait retryAfter]
H --> I{Still limited?}
I -- No --> C
I -- Yes --> J[Wait >= 250ms]
J --> K{Timed out?}
K -- No --> H
K -- Yes --> E
Violation escalation is stored under violation:{key} and uses these defaults:
| Option | Default | Meaning |
|---|---|---|
maxViolations |
5 |
Maximum escalation steps. |
escalationMultiplier |
2 |
Multiplier per repeated violation. |
resetAfter |
1h |
TTL for violation state. |
escalate |
true when violations is set |
Set false to disable escalation. |
Formula:
cooldown = baseRetryAfter * multiplier^(count - 1)
If escalation produces a later resetAt than the algorithm returned, the result is updated so resetAt and retryAfter stay accurate.
Bypass order is always:
bypass.userIds,bypass.guildIds, andbypass.roleIds.- Temporary exemptions stored in storage.
bypass.check(source).
Bypass example:
configureRatelimit({
bypass: {
userIds: ['USER_ID'],
guildIds: ['GUILD_ID'],
roleIds: ['ROLE_ID'],
check: (source) => source.channelId === 'ALLOWLIST_CHANNEL',
},
});Temporary exemptions:
import { grantRateLimitExemption } from '@commandkit/ratelimit';
await grantRateLimitExemption({
scope: 'user',
id: 'USER_ID',
duration: '1h',
});Listing behavior:
listRateLimitExemptions({ scope, id })reads a single key directly.listRateLimitExemptions({ scope })scans by prefix and requireskeysByPrefix.expiresInMsisnullwhenttlis not supported.
| Source | Conditions | Action |
|---|---|---|
| Message | Channel is sendable | reply() with cooldown embed. |
| Interaction | Repliable and not replied/deferred | reply() with ephemeral cooldown embed. |
| Interaction | Repliable and already replied/deferred | followUp() with ephemeral cooldown embed. |
| Interaction | Not repliable | No response. |
The default embed title is :hourglass_flowing_sand: You are on cooldown and the description uses a relative timestamp based on resetAt.
| Hook | Called when | Notes |
|---|---|---|
onAllowed |
Command is allowed | Receives the first result. |
onRateLimited |
Command is limited | Receives the first limited result. |
onViolation |
A violation is recorded | Receives key and violation count. |
onReset |
resetRateLimit succeeds |
Not called by resetAllRateLimits. |
onStorageError |
Storage operation fails | fallbackUsed is false in runtime plugin paths. |
The runtime plugin calls ctx.commandkit.analytics.track(...) with:
| Event name | When |
|---|---|
ratelimit_allowed |
After an allowed consume. |
ratelimit_hit |
After a limited consume. |
ratelimit_violation |
When escalation records a violation. |
A ratelimited event is emitted on the ratelimits channel:
commandkit.events
.to('ratelimits')
.on('ratelimited', ({ key, result, source, aggregate, commandName, queued }) => {
console.log(key, commandName, queued, aggregate.retryAfter);
});Payload fields include key, result, source, aggregate, commandName, and queued.
resetRateLimit clears the base key, its violation: key, and any window variants. It accepts either a raw key or a scope-derived key.
| Mode | Required params | Notes |
|---|---|---|
| Direct | key |
Resets key, violation:key, and window variants. |
| Scoped | scope + commandName + required ids |
Throws if identifiers are missing. |
resetAllRateLimits supports several modes and requires storage delete helpers:
| Mode | Required params | Storage requirement |
|---|---|---|
| Pattern | pattern |
deleteByPattern |
| Prefix | prefix |
deleteByPrefix |
| Command name | commandName |
deleteByPattern |
| Scope | scope + required ids |
deleteByPrefix |
When a command file is hot-reloaded, the plugin deletes keys that match:
*:{commandName}violation:*:{commandName}*:{commandName}:w:*violation:*:{commandName}:w:*
HMR reset requires deleteByPattern. If the storage does not support pattern deletes, nothing is cleared.
The compiler plugin (UseRateLimitDirectivePlugin) uses CommonDirectiveTransformer with directive = "use ratelimit" and importName = "$ckitirl". It transforms async functions only.
The runtime wrapper:
- Uses the runtime default limiter (merged with
DEFAULT_LIMITER). - Generates a per-function key
rl:fn:{uuid}and applieskeyPrefixif present. - Aggregates results across windows and throws
RateLimitErrorwhen limited. - Caches the wrapper per function and exposes it as
globalThis.$ckitirl.
Example:
import { RateLimitError } from '@commandkit/ratelimit';
const heavy = async () => {
'use ratelimit';
return 'ok';
};
try {
await heavy();
} catch (error) {
if (error instanceof RateLimitError) {
console.log(error.result.retryAfter);
}
}| Setting | Default |
|---|---|
maxRequests |
10 |
interval |
60s |
algorithm |
fixed-window |
scope |
user |
DEFAULT_KEY_PREFIX |
rl: |
RATELIMIT_STORE_KEY |
ratelimit |
roleLimitStrategy |
highest |
queue.maxSize |
3 |
queue.timeout |
30s |
queue.deferInteraction |
true |
queue.ephemeral |
true |
queue.concurrency |
1 |
initializeDefaultStorage |
true |
- If no storage is configured and default storage is disabled, the plugin logs once and stores an empty result without limiting.
- If no scope key can be resolved, the plugin stores an empty result and skips limiting.
- If storage errors occur during consume,
onStorageErroris invoked and the plugin skips limiting for that execution. - For token and leaky buckets,
limitequalsburst. For fixed and sliding windows,limitequalsmaxRequests.
DurationLike accepts numbers (milliseconds) or strings parsed by ms, plus custom units for weeks and months.
| Unit | Meaning |
|---|---|
ms, s, m, h, d |
Standard ms units. |
w, week, weeks |
7 days. |
mo, month, months |
30 days. |
| Export | Description |
|---|---|
ratelimit |
Plugin factory returning compiler + runtime plugins. |
RateLimitPlugin |
Runtime plugin class. |
UseRateLimitDirectivePlugin |
Compiler plugin for use ratelimit. |
RateLimitEngine |
Algorithm coordinator with escalation handling. |
| Algorithm classes | Fixed, sliding, token bucket, and leaky bucket implementations. |
| Storage classes | Memory, Redis, and fallback storage. |
| Runtime helpers | configureRatelimit, setRateLimitStorage, getRateLimitRuntime, and more. |
| API helpers | getRateLimitInfo, resets, and exemption helpers. |
RateLimitError |
Error thrown by the directive wrapper. |
Subpath exports:
@commandkit/ratelimit/redis@commandkit/ratelimit/memory@commandkit/ratelimit/fallback