Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ency07/B2B/llms.txt

Use this file to discover all available pages before exploring further.

checkRateLimit (src/lib/utils/rate-limiter.ts) provides lightweight in-process rate limiting for Server Actions. It uses an in-memory Map to track request counts per identifier with a configurable time window. It is used in the wizard to prevent form submission abuse.
This is an in-process rate limiter. In serverless deployments (Vercel), each function instance has its own isolated Map, so the effective rate limit is per-instance, not globally enforced. For global rate limiting, use an edge middleware solution (e.g., Upstash Redis).

API Reference

checkRateLimit(identifier, maxRequests?, windowMs?)

checkRateLimit(
  identifier: string,
  maxRequests: number = 10,
  windowMs: number = 60_000
): { allowed: boolean; remaining: number }
identifier
string
required
Unique key for the rate-limited entity. Typically "action:email" or "action:ip". Example: "wizard:user@company.com"
maxRequests
number
Maximum number of requests allowed in the window. Defaults to 10.
windowMs
number
Window duration in milliseconds. Defaults to 60_000 (1 minute).
allowed
boolean
true if the request is within the rate limit; false if the limit is exceeded.
remaining
number
Number of remaining requests in the current window. Returns 0 when the rate limit is exceeded.

Usage in the Wizard

import { checkRateLimit } from "@/lib/utils/rate-limiter";

const identifier = `wizard:${rawData.email}`;
const { allowed } = checkRateLimit(identifier, 10, 60_000);
if (!allowed) {
  throw new Error("Demasiadas solicitudes. Intente nuevamente en un minuto.");
}

How It Works

Entries are stored internally as { count: number; resetAt: number } in a module-level Map. On each call to checkRateLimit:
  1. Window expired — if no entry exists for the identifier, or if Date.now() > entry.resetAt, the entry is reset to { count: 1, resetAt: now + windowMs } and the request is allowed.
  2. Limit reached — if entry.count >= maxRequests, returns { allowed: false, remaining: 0 } without incrementing the counter.
  3. Within window — if the entry is active and below the limit, count is incremented and { allowed: true, remaining: maxRequests - entry.count } is returned.
Because the window resets only on the first request after expiry (not on a fixed schedule), the implementation behaves as a sliding reset rather than a strict fixed-window counter.

Build docs developers (and LLMs) love