Rate limiting

A fixed window: each key has a counter that resets after the window. check(key, { max, windowSecs }) returns allowed / remaining / retry-after. Here a burst of 6 requests against a limit of 3 — the first three pass, the rest are blocked with a retry delay.

Tip
In production, derive the key with keyForRequest(request, bucket) (client IP + bucket name) and back it with a store shared across workers. attempt(key, opts, fn) runs fn only when under the limit; reset(key) clears the counter (e.g. after a successful login).

Run the burst to watch the limit kick in.

src/app/examples/rate-limit/page.tsxTSX
import { createRateLimiter, memoryRateStore, keyForRequest } from "@bext-stack/framework/rate-limit";

const limiter = createRateLimiter({ store: memoryRateStore() });

export async function action({ request }) {
  const key = keyForRequest(request, "signup");   // client ip + bucket
  const r = await limiter.check(key, { max: 5, windowSecs: 60 });
  if (!r.allowed) {
    return new Response("Too many requests", {
      status: 429, headers: { "retry-after": String(r.retryAfterSecs) },
    });
  }
  // …handle the request…
}