# Rate Limiting Sliding window rate limiting for Next.js 14 API routes — in-memory for development, drop-in Upstash Redis upgrade for production, with per-IP and per-user strategies, standard headers, and pre-tuned configs for auth, purchases, AI, and webhooks. ## What's included **Configuration** - `RATE_LIMIT_CONFIG` — per-endpoint limits for `api`, `auth`, `auth_register`, `purchase`, `ai_customize`, `webhook`, `payout`, `search`; edit values here to change limits globally - `RateLimitKey` — union type of all config keys **In-memory store (dev / single instance)** - `checkRateLimit(identifier, type)` — increments hit count and returns a `RateLimitResult`; resets after the window expires; auto-cleans expired entries every 5 minutes - `peekRateLimit(identifier, type)` — reads current state without incrementing; use for dashboard displays - `resetRateLimit(identifier, type)` — clears a key; use after successful auth to reset brute-force counters **Response helpers** - `rateLimitHeaders(result)` — returns `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` as a plain object - `tooManyRequests(result)` — returns a `NextResponse` with status 429, JSON body, and rate limit headers - `getIP(req)` — extracts real IP from `cf-connecting-ip` → `x-forwarded-for` → `x-real-ip` → `'anonymous'` **Route wrappers** - `withRateLimit(type, handler, options?)` — wraps a route handler; rate-limits by IP by default; accepts `getIdentifier` to use any custom key; appends headers to successful responses - `withAuthRateLimit(type, handler)` — same as above but extracts user ID from the NextAuth JWT cookie; falls back to IP if unauthenticated **Production (Upstash Redis)** - `checkRateLimitRedis(identifier, type)` — same interface as `checkRateLimit` but backed by Upstash sliding window; `RedisLimiters` map is exported for direct access; the entire Redis block is commented out — uncomment when ready ## Setup ### 1. Install dependencies ```bash # Development — no extra deps needed # Production only npm install @upstash/ratelimit @upstash/redis ``` ### 2. Environment variables ``` # Production only (Upstash) UPSTASH_REDIS_REST_URL=your Upstash Redis REST URL UPSTASH_REDIS_REST_TOKEN=your Upstash Redis REST token # Required only if using withAuthRateLimit NEXTAUTH_SECRET=your NextAuth secret ``` ### 3. Add to your project ```ts import { withRateLimit } from '@/blocks/ratelimit' ``` No other configuration needed for dev. For production, uncomment the Redis block at the bottom of the file and swap `checkRateLimit` calls for `checkRateLimitRedis`. ## Usage examples ```ts // Per-IP rate limiting — wrap the handler directly import { withRateLimit } from '@/blocks/ratelimit' import { NextResponse } from 'next/server' export const POST = withRateLimit('auth', async (req) => { // handle sign-in attempt return NextResponse.json({ ok: true }) }) ``` ```ts // Per-user rate limiting on an authenticated route import { withAuthRateLimit } from '@/blocks/ratelimit' import { NextResponse } from 'next/server' export const POST = withAuthRateLimit('ai_customize', async (req) => { const result = await runAI(req) return NextResponse.json({ result }) }) ``` ```ts // Manual check — when you need the result inline before doing work import { checkRateLimit, tooManyRequests, resetRateLimit } from '@/blocks/ratelimit' export async function POST(req) { const result = checkRateLimit(session.user.id, 'purchase') if (!result.success) return tooManyRequests(result) const order = await createOrder(session.user.id) // Reset after a successful purchase so the user isn't penalised // for legitimate retries on a different item resetRateLimit(session.user.id, 'purchase') return Response.json(order) } ``` ## Notes - The in-memory store does not survive Lambda cold starts or scale across multiple Vercel instances — on Vercel, each serverless function instance has its own store; this is fine for development and low-traffic deployments, but switch to Upstash before going to production at any meaningful scale - `withAuthRateLimit` dynamically imports `next-auth/jwt` — if you're not using NextAuth, replace the `getIdentifier` function with your own session extraction logic - `RATE_LIMIT_CONFIG` is `as const` — to override a limit for a specific route without changing the global config, pass a custom `getIdentifier` that appends a suffix to the key (e.g. `userId + ':strict'`) and add that key to the config - The `webhook` config is set to 500 req/min intentionally — Razorpay/PayPal can fire bursts of retries; tightening this will cause missed payment events