Skip to content
UniKVS
Esc
↑↓navigate↵open⌘Jpreview
On this page

@unikvs/rate-limit

Middleware that limits operation frequency with a token bucket.

Overview

RateLimit limits operation frequency with a token bucket, protecting storage from excessive reads and writes.Middleware

Use it to allow bursts while capping the average rate. On exhaustion, choose waiting or an immediate error.

  • name is RateLimit.
  • isOpen is always true.
  • The bucket is shared per middleware instance (not per key), consuming 1 token for every operation (set / get / stream / has / delete / copy / move / clear).
  • encode / decode / encodeStream / decodeStream pass data through.

Install with:

npm install @unikvs/rate-limit
pnpm add @unikvs/rate-limit
yarn add @unikvs/rate-limit
bun add @unikvs/rate-limit
nub add @unikvs/rate-limit
aube add @unikvs/rate-limit

Usage

RateLimit implements IMiddleware. Pass it to UniKvs with use().

import { RateLimit } from "@unikvs/rate-limit";

Constructor arguments:

PropType
tokensPerInterval?number

Tokens refilled per interval.

Typenumber
Default1
intervalMs?number

Refill interval (ms).

Typenumber
Default1000
maxTokens?number

Maximum tokens held. Defaults to max(1, tokensPerInterval). An explicit value below 1 fails construction.

Typenumber
burst?number

Alias of maxTokens. When both are given, maxTokens wins. An explicit value below 1 fails construction.

Typenumber
strategy?RateLimitStrategy

Behavior on exhaustion. "wait" waits for refill; "throw" throws immediately.

TypeRateLimitStrategy
Default"wait"
now?() => number

Returns the current time (epoch ms). Defaults to Date.now.

Type() => number
DefaultDate.now
sleep?IRateLimitSleep

Wait implementation. Uses setTimeout by default; swap it to control time in tests.

TypeIRateLimitSleep

The following caps usage at 5 operations per second, waiting on exhaustion:

import { Memory } from "@unikvs/memory";
import { RateLimit } from "@unikvs/rate-limit";
import * as unikvs from "unikvs";

const kvs = new unikvs.Builder<{ k1: unikvs.PlainValue<string> }>()
  .use(new RateLimit({ tokensPerInterval: 5, intervalMs: 1000 }))
  .persist(new Memory())
  .build();

await kvs.open();
await kvs.set("k1", "hello");
console.log(await kvs.get("k1")); // "hello"
await kvs.close();

RateLimit

Middleware that limits operation frequency with a token bucket. It extends PassThrough and performs no data conversion.

RateLimitOptions

Options accepted by the RateLimit constructor. Specifies token counts, refill interval, and exhaustion behavior.

RateLimitStrategy

Behavior on exhaustion. "wait" waits until tokens refill; "throw" throws immediately.

IRateLimitSleep

Wait implementation. Swap it to control time in tests.

Data types

Method Input Output
encode any any
decode any any
encodeStream any any
decodeStream any any
  • use consumes one token per operation and never touches data.
  • encode / decode / encodeStream / decodeStream pass input through unchanged.
  • Tokens refill with elapsed time and never exceed the cap. A clock moving backward never adds tokens.
  • When maxTokens is omitted, the capacity is max(1, tokensPerInterval). Since each operation consumes one token, a tokensPerInterval below 1 still yields a capacity of 1.
  • An explicit maxTokens or burst below 1 throws RateLimitInvalidOptionError at construction. tokensPerInterval may be a fractional value below 1.
  • The wait strategy waits until recovery, serving requests FIFO. It aborts waiting on the abort signal.

Errors

Errors thrown by this package:

RateLimitExceededError

PropType
retryAfterMsnumber

Time to wait before retrying (ms).

Typenumber

Meaning

Thrown when an operation arrives with no tokens left and strategy is "throw". next is not called and the operation never runs.

How to fix

Wait meta.retryAfterMs before retrying, or use strategy: "wait" for automatic waiting. If this happens often, revisit tokensPerInterval or maxTokens.

Example

Catching RateLimitExceededError:

import { RateLimitExceededError } from "@unikvs/rate-limit";

try {
  await kvs.set("k1", "hello");
} catch (ex) {
  if (ex instanceof RateLimitExceededError) {
    console.log(ex.meta); // { retryAfterMs: 500 }
  } else {
    throw ex;
  }
}

RateLimitInvalidOptionError

PropType
namestring

The offending option name.

Typestring
actualunknown

The given value.

Typeunknown

Meaning

Thrown when the constructor receives invalid options: non-positive-finite numbers, an explicit maxTokens or burst below 1, a strategy other than "wait" / "throw", or a non-function now / sleep.

How to fix

Check meta.name: pass positive finite numbers, a valid enum value, or functions. When both maxTokens and burst are given, maxTokens wins.

Example

Catching RateLimitInvalidOptionError:

import { RateLimit, RateLimitInvalidOptionError } from "@unikvs/rate-limit";

try {
  const limiter = new RateLimit({ intervalMs: 0 });
} catch (ex) {
  if (ex instanceof RateLimitInvalidOptionError) {
    console.log(ex.meta); // { name: "intervalMs", actual: 0 }
  } else {
    throw ex;
  }
}

AbortError from the abort signal propagates as-is, and this middleware consumes no token in that case.

Was this page helpful?