Rate Limiting

Fixed-window rate limiting backed by Cloudflare KV, protecting login, MFA, checkout, and payment webhook endpoints.

A fixed-window rate limiter backed by Cloudflare KV. The algorithm lives once, in packages/rate-limit, over a small counter store. Each Worker ties it to its own KV through a thin wrapper:

  • apps/app/server/utils/rate-limit.ts — binds the algorithm to hubKV() inside the App Worker's Nitro runtime.
  • apps/functions/src/rate-limit.ts — binds it to the raw KVNamespace (AuthKV), since the auth Worker has no Nitro/NuxtHub to call hubKV() from.

The two Workers keep separate counters: same algorithm, different namespaces.

packages/rate-limit/core.ts
export async function rateLimit(store, key, { limit, windowSeconds }, now = Date.now) {
  // Cloudflare KV rejects a TTL under 60s, so the window can't be shorter.
  const window = Math.max(windowSeconds, MIN_WINDOW_SECONDS);
  const storeKey = `ratelimit:${key}:${Math.floor(now() / 1000 / window)}`;
  const count = ((await store.get(storeKey)) ?? 0) + 1;
  await store.set(storeKey, count, window);
  return { allowed: count <= limit, count, limit };
}

The store is a two-method port (get, set with a TTL). kvNamespaceStore and hubKvStore in the same package adapt the two KV shapes to it. Read, increment and write are not atomic, so concurrent calls can undercount: the limit is a brake, not an exact quota.

The App Worker wrapper exports rateLimit(key, rule) and assertNotRateLimited(event, routeKey, options). The second one is for HTTP routes: it keys by <routeKey>:<ip> and throws 429 Too Many Requests directly when the limit is exceeded.

What's currently protected

WhereKeyed byPurpose
apps/functions/src/auth.ts (enforceCredentialRateLimit)path + IP + emailPassword/code login on the auth Worker
layers/account/server/api/mfa/verify.post.tsuser idAccount MFA enrollment attempts
layers/account/server/api/mfa/challenge.post.tsuser idAccount MFA challenge attempts
layers/account/server/api/mfa/recovery-codes.post.tsuser idRecovery code regeneration
layers/billing/server/trpc/checkout.tsIPStarting a purchase without an account
layers/billing/server/api/webhooks/stripe.post.tsIPStripe webhook, on top of signature validation

This isn't a general-purpose API throttle — it's applied specifically where credential/signature validation alone doesn't stop a high-volume replay: authentication attempts, MFA codes and unauthenticated webhook or checkout endpoints. There's no rate limiting on ordinary tRPC procedures today.

Nuxfire Production Kit

Ready to build and launch your SaaS?

Get 100% full source code ownership, no proprietary wrappers, with architecture ready for millions of requests on Cloudflare.

© 2026 Nuxfire