import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
// Configurare client Upstash Ratelimit
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10 s"), // max 10 cereri la 10 secunde
analytics: true,
});
export async function handleRequest(userId: string) {
const { success, limit, reset, remaining } = await ratelimit.limit(userId);
if (!success) {
return {
status: 429,
body: "Too Many Requests. Calmează-te puțin.",
headers: {
"X-RateLimit-Limit": limit.toString(),
"X-RateLimit-Remaining": remaining.toString(),
"X-RateLimit-Reset": reset.toString(),
}
};
}
return { status: 200, body: "Acces permis" };
}Salutare. Am văzut destule proiecte care pornesc la drum fără niciun fel de limitare pe API, iar apoi echipa se miră de ce baza de date crapă când un „băiat deștept” începe să facă scraping agresiv pe endpoint-ul de search. Sau, mai rău, când vine o factură de 500$ de la vreun LLM pentru că cineva a buclat un script de test pe un endpoint neprotejat. Am pățit-o și eu, nu vă îngrijorați.
Rate limiting-ul nu e opțional, dar unde îl pui? La nivel de infrastructură (Nginx), în memorie direct în aplicație, sau distribuit (Redis/Upstash)? Fiecare abordare vine cu plusuri și minusuri mari pe care e bine să le știi înainte să scrii prima linie de cod.
1. Nginx: Primul scut, dar cam orb
Dacă ai control pe server (un VPS, un EC2 clasic), Nginx e genial pentru a opri atacurile brute-force sau scripturile simple de scraping înainte ca traficul să ajungă la codul tău Node sau Go. Configurezi un limit_req_zone pe IP și ai rezolvat problema cu 0% efort de dezvoltare.
Merge brici pentru protecția endpoint-urilor publice precum cele de login sau register. Totuși, marele minus e că Nginx e destul de „orb”. Identifică utilizatorul doar după IP. Dacă ai 100 de oameni care accesează API-ul din aceeași rețea de corporație sau facultate (în spatele aceluiași NAT), Nginx îi va vedea ca pe un singur vizitator și le va bloca accesul eronat.
Am avut un caz în care un client a rulat un script de sincronizare de pe 5 servere diferite și ne-a îngenuncheat baza de date, deși Nginx limita IP-urile individual. Pur și simplu aveau destule IP-uri publice ca să ocolească blocajul.
2. In-Memory: Bun pentru început, nasol la scalare
Cea mai simplă variantă în cod: ții un obiect în memoria RAM a aplicației (cum e express-rate-limit în Node). Am folosit abordarea asta pe vremuri la un proiect mic, cu vreo 8k useri activi, rulat pe o singură instanță de PM2. Zero latență de rețea, zero costuri suplimentare.
Marea problemă apare când crești. Dacă ai nevoie de load balancer și rulezi 3 instanțe ale aplicației în Docker, fiecare instanță își va ține propriul contor în memorie. Un user poate face de 3 ori mai multe request-uri decât ai vrut tu, în funcție de cum îl trimite load balancer-ul. În plus, la fiecare deployment sau restart de container, istoricul se șterge complet și o iei de la capăt.
3. Redis / Upstash: Standardul pentru aplicații serioase
Dacă vrei rate limiting inteligent, bazat pe ID-ul de utilizator din JWT și nu pe IP, ai nevoie de o bază de date rapidă în memorie. Redis e regele aici. Folosind un algoritm de tip sliding window counter, poți fi extrem de precis. Spre deosebire de fixed window (unde un utilizator poate face 100 de cereri la 23:59 și încă 100 la 00:01), sliding window calculează exact pe durata ultimelor N secunde.
Pentru arhitecturile serverless (Next.js pe Vercel, AWS Lambda), Redis clasic e greu de gestionat din cauza conexiunilor persistente care ating rapid limita maximă. Aici intervine Upstash (care e practic un Redis serverless accesibil prin HTTP). Am implementat Upstash la un API de generat rapoarte PDF și am rezolvat elegant problema abuzurilor pe bază de API Key.
Trade-off-ul? Fiecare request către API-ul tău trebuie să facă un round-trip rapid către Redis înainte de a procesa logica de business. Asta adaugă cam 10-25ms la timpul total de răspuns. Pentru majoritatea aplicațiilor e insesizabil, dar e un cost pe care trebuie să-l asumi.
Voi ce folosiți în producție? Mergeți pe varianta simplă din Nginx sau aveți Redis configurat pentru fiecare endpoint sensibil?