Skip to main content

RateLimiter Plugins

withRateLimiterPrefix plugin​

The RateLimiter prefix plugin intercepts calls to a rate-limiter adapter and transparently prefixes all rate-limiter keys with a configurable string. This enables logical key namespacing without modifying the adapter implementation.

Use cases​

  • Multi-tenant rate limiting — Prefix rate-limiter keys with a tenant identifier to apply separate rate limits per tenant
  • Endpoint scoping — Organize rate limits by API endpoint or route prefix
  • Environment isolation — Separate development, staging, and production rate limit state
  • User tier differentiation — Prefix keys with a tier identifier (e.g., "free:", "premium:") to apply different rate limits

How it works​

The withRateLimiterPrefix function returns a PluginFn that calls enhance on each adapter method that accepts a rate-limiter key. When an enhanced method is invoked, the plugin intercepts the call, prepends the configured prefix to the key argument, and forwards the modified arguments to the original method.

The plugin prefixes keys for the following methods:

MethodKey argumentPattern
getStateFirst argument (key)prefix + key
resetFirst argument (key)prefix + key
updateStateFirst argument (key)prefix + key

Usage​

./samples/with-rate-limiter-prefix.ts
import { withPlugin } from "eridu-tech/middleware";
import { MemoryRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/memory-rate-limiter-storage-adapter";
import { DatabaseRateLimiterAdapter } from "eridu-tech/rate-limiter/database-rate-limiter-adapter";
import { withRateLimiterPrefix } from "eridu-tech/rate-limiter/plugins";

const adapter = new DatabaseRateLimiterAdapter({
adapter: new MemoryRateLimiterStorageAdapter(),
});

// Apply the prefix plugin to the adapter
const prefixedAdapter = withPlugin(
adapter,
withRateLimiterPrefix("tenant-42:"),
);

Before/after behavior​

Before — Rate-limiter keys are used as-is:

./samples/unprefixed-get-state.ts
import { MemoryRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/memory-rate-limiter-storage-adapter";
import { DatabaseRateLimiterAdapter } from "eridu-tech/rate-limiter/database-rate-limiter-adapter";

const adapter = new DatabaseRateLimiterAdapter({
adapter: new MemoryRateLimiterStorageAdapter(),
});

await adapter.getState("api:login");
// -> checks rate limit for "api:login"

After — Rate-limiter keys are automatically prefixed:

./samples/prefixed-get-state.ts
import { withPlugin } from "eridu-tech/middleware";
import { MemoryRateLimiterStorageAdapter } from "eridu-tech/rate-limiter/memory-rate-limiter-storage-adapter";
import { DatabaseRateLimiterAdapter } from "eridu-tech/rate-limiter/database-rate-limiter-adapter";
import { withRateLimiterPrefix } from "eridu-tech/rate-limiter/plugins";

const adapter = new DatabaseRateLimiterAdapter({
adapter: new MemoryRateLimiterStorageAdapter(),
});

// Apply the prefix plugin to the adapter
const prefixedAdapter = withPlugin(
adapter,
withRateLimiterPrefix("tenant-42:"),
);

await prefixedAdapter.getState("api:login");
// -> checks rate limit for "tenant-42:api:login"
danger

Because withPlugin uses enhance under the hood, the same edge case applies: if one enhanced method internally calls another enhanced method via this, the middleware will apply twice. Be mindful of inter-method calls when applying plugins that enhance multiple methods on the same instance.

info

For more information about the withPlugin function and applying plugins to adapters, see the Middleware plugin documentation.