Das Kit verwendet Upstash Redis als Backbone für Caching und Rate Limiting. Das System bietet kategoriebasiertes API-Rate-Limiting (Upload, E-Mail, Kontakt, Zahlungen, Webhooks), stufenbasierte KI-Kontingente (Free bis Enterprise) sowie ein ausfallsicheres Design, das bei nicht verfügbarem Redis graceful degradiert.
Diese Seite behandelt die Einrichtung, die Rate-Limiting-Architektur, KI-Kontingente und Debugging-Tools. Zur Sicherheitsübersicht siehe Sicherheit. Für KI-spezifisches Kostenmanagement siehe Kostenmanagement.
Funktionsweise
Rate Limiting verwendet eine zweischichtige Architektur — ein flüchtiger In-Memory-Cache reduziert Redis-Aufrufe um 50–80 %, und Redis stellt die persistenten Sliding-Window-Zähler bereit:
API-Anfrage
|
v
Rate-Limit-Middleware (withRateLimit)
|--- Extrahiert userId (Clerk) und IP (Header)
|
v
Ephemeral Cache (In-Memory-Map)
|--- Treffer? → Gecachtes Ergebnis zurückgeben (kein Redis-Aufruf)
|--- Kein Treffer? → Weiter zu Redis
|
v
Upstash Redis (Sliding-Window-Algorithmus)
|--- Prüft benutzerbezogenes Limit (wenn userId verfügbar)
|--- Prüft IP-bezogenes Limit (wenn IP verfügbar)
|--- Gibt zurück: { success, limit, remaining, reset }
|
v
Antwort
|--- 200 OK + X-RateLimit-*-Header (wenn erlaubt)
|--- 429 Too Many Requests (wenn Rate Limit überschritten)
Der ephemere Cache ist eine modulübergreifende
Map, die über Anfragen innerhalb derselben Serverless-Instanz hinweg bestehen bleibt. Das ist unbedenklich, da Rate Limiting von Natur aus näherungsweise ist — einige zusätzliche Anfragen während der Cache-Revalidierung sind akzeptabel.Einrichtung
1
Upstash-Redis-Datenbank erstellen
Registriere dich auf upstash.com und erstelle eine neue Redis-Datenbank. Wähle die Region, die deinem Deployment am nächsten liegt. Der kostenlose Plan umfasst 500.000 Befehle/Monat — ausreichend für die meisten SaaS-Anwendungen.
2
Umgebungsvariablen setzen
Kopiere die REST-URL und das Token aus deinem Upstash-Dashboard und füge sie zu
apps/boilerplate/.env.local hinzu:bash
UPSTASH_REDIS_REST_URL=https://your-database.upstash.io
UPSTASH_REDIS_REST_TOKEN=AXxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Redis ist optional. Wenn keine Zugangsdaten konfiguriert sind, protokolliert das System eine Warnung und lässt alle Anfragen durch (Fail-Open). Das bedeutet, du kannst lokal ohne Redis entwickeln — Rate Limiting wird dann einfach nicht durchgesetzt.
API-Rate-Limiting
Der API-Rate-Limiter verwendet ein kategoriebasiertes System, bei dem jede API-Kategorie unabhängige Limits für benutzer- und IP-basiertes Tracking hat:
src/lib/security/api-rate-limiter.ts — API_LIMITS Configuration
const API_LIMITS: Record<
APICategory,
{ user?: RateLimitConfig; ip?: RateLimitConfig }
> = {
upload: {
user: { requests: 10, window: '1 h', identifier: 'user' },
ip: { requests: 20, window: '1 h', identifier: 'ip' },
},
email: {
user: { requests: 5, window: '1 h', identifier: 'user' },
ip: { requests: 10, window: '1 h', identifier: 'ip' },
},
contact: {
ip: { requests: 3, window: '1 h', identifier: 'ip' },
},
payments: {
user: { requests: 20, window: '1 h', identifier: 'user' },
},
webhooks: {
ip: { requests: 100, window: '1 h', identifier: 'ip' },
},
api: {
user: { requests: 100, window: '1 h', identifier: 'user' },
ip: { requests: 200, window: '1 h', identifier: 'ip' },
},
}
Rate-Limit-Kategorien
| Kategorie | Benutzer-Limit | IP-Limit | Anwendungsfall |
|---|---|---|---|
upload | 10 Req/Stunde | 20 Req/Stunde | Datei-Upload-Endpunkt |
email | 5 Req/Stunde | 10 Req/Stunde | E-Mail-Versand-Endpunkte |
contact | — | 3 Req/Stunde | Kontaktformular (nur IP, keine Auth erforderlich) |
payments | 20 Req/Stunde | — | Zahlungs- und Checkout-Endpunkte |
webhooks | — | 100 Req/Stunde | Webhook-Verarbeitung (hoher Durchsatz) |
api | 100 Req/Stunde | 200 Req/Stunde | Allgemeine API-Endpunkte (Catch-All) |
Jede Kategorie kann ein benutzerbasiertes Limit, ein IP-basiertes Limit oder beides haben. Wenn beide konfiguriert sind, muss die Anfrage beide Prüfungen bestehen.
Rate-Limit-Middleware verwenden
Die
withRateLimit-Middleware-Factory umhüllt API-Route-Handler mit automatischem Rate Limiting:src/lib/security/rate-limit-middleware.ts — withRateLimit Signature
export function withRateLimit(
category: APICategory,
handler: (request: NextRequest) => Promise<NextResponse>
): (request: NextRequest) => Promise<NextResponse> {
Verwendung in API-Routen
typescript
import { withRateLimit } from '@/lib/security/rate-limit-middleware'
// Handler mit einer Rate-Limit-Kategorie umhüllen
export const POST = withRateLimit('upload', async (request) => {
// Deine Handler-Logik — wird nur ausgeführt, wenn das Rate Limit nicht überschritten ist
return NextResponse.json({ success: true })
})
Die Middleware erledigt automatisch:
- Extrahiert die Benutzer-ID aus der Clerk-Authentifizierung
- Extrahiert die Client-IP aus
x-forwarded-for- oderx-real-ip-Headern - Prüft sowohl Benutzer- als auch IP-Rate-Limits für die Kategorie
- Gibt
429 Too Many Requestsmit Retry-Informationen zurück, wenn überschritten - Fügt
X-RateLimit-*-Header zu allen Antworten hinzu
KI-Rate-Limiting
Das KI-System hat einen eigenen dedizierten Rate-Limiter mit stufenbasierten Monatskontingenten und globalem Burst-Schutz:
Stufenbasierte Monatskontingente
| Stufe | Monatliche Anfragen | Zeitfenster | Override-Variable |
|---|---|---|---|
| Free | 20 | 30 Tage | AI_FREE_TIER_REQUESTS |
| Basic | 100 | 30 Tage | AI_BASIC_TIER_REQUESTS |
| Pro | 1.000 | 30 Tage | AI_PRO_TIER_REQUESTS |
| Enterprise | 10.000 | 30 Tage | AI_ENTERPRISE_TIER_REQUESTS |
Dreistufige Prüfung
Jede KI-Anfrage durchläuft drei aufeinander folgende Prüfungen:
- Globaler Burst-Schutz — 10 Anfragen pro 10 Sekunden (konfigurierbar über
AI_RATE_LIMIT_MAX_REQUESTSundAI_RATE_LIMIT_WINDOW). Dies verhindert, dass ein einzelner Benutzer die KI-Endpunkte überflutet. - Credit-Guthaben-Prüfung — Wenn das Credit-System aktiviert ist (
NEXT_PUBLIC_PRICING_MODE=credits), prüft das System, ob der Benutzer ausreichend Credits für den Vorgang hat. - Automatisches Zurücksetzen — Wenn seit dem letzten Credit-Reset 30 oder mehr Tage vergangen sind, werden Credits automatisch zurückgesetzt — als Backup für verpasste Webhook-Events.
Antwort-Header
Jede rate-limitierte Antwort enthält Standard-Header, damit Clients ihre Nutzung verfolgen können:
| Header | Wert | Beschreibung |
|---|---|---|
X-RateLimit-Limit | 100 | Maximale Anfragen im Zeitfenster |
X-RateLimit-Remaining | 87 | Verbleibende Anfragen im aktuellen Zeitfenster |
X-RateLimit-Reset | 1708012800000 | Unix-Zeitstempel (ms) für den Reset des Zeitfensters |
Wenn das Limit überschritten wird, enthält der
429-Antwort-Body:json
{
"error": "Rate limit exceeded",
"message": "Too many requests. Please try again in 45 minutes.",
"retryAfter": "45 minutes",
"limit": 10,
"remaining": 0,
"reset": 1708012800000
}
Server-seitige Caching-Muster
Über das Rate Limiting hinaus verwendet das Kit modulübergreifendes Caching für Konfigurationsdaten, die sich selten ändern, aber häufig gelesen werden:
typescript
// Beispiel: Caching der Pricing-Konfiguration
// Modulübergreifende Variable bleibt über Anfragen in derselben Serverless-Instanz erhalten
let cachedPricingConfig: PricingConfig | null = null
let lastFetched = 0
const CACHE_TTL = 5 * 60 * 1000 // 5 Minuten
export async function getPricingConfig(): Promise<PricingConfig> {
const now = Date.now()
if (cachedPricingConfig && now - lastFetched < CACHE_TTL) {
return cachedPricingConfig
}
cachedPricingConfig = await fetchPricingFromDB()
lastFetched = now
return cachedPricingConfig
}
Dieses Muster wird für die Zahlungsplan-Konfiguration, Credit-Kostentabellen und andere Daten verwendet, die sich selten ändern. Es vermeidet Datenbankabfragen bei jeder Anfrage, ohne Redis zu benötigen.
Für kritische Konfigurationen wie Pricing-Stufen und Billing-Varianten-IDs verwendet das Kit eine 4-schichtige Validierungsarchitektur: Umgebungs-Laden → Zod-Schema-Validierung → Cache-Speicherung → TTL-basierte Invalidierung. Das Zod-Schema validiert ALLE erforderlichen Felder vor dem Cachen — dies verhindert, dass teilweise geladene Pricing-Daten ausgeliefert werden, was Zahlungsabläufe unterbrechen würde. Siehe
apps/boilerplate/src/lib/payments/config.ts und apps/boilerplate/src/lib/credits/config.ts für die Implementierung.Modulübergreifende
Map: Für Daten, die sich selten ändern und Veraltung tolerieren können (Pricing-Konfiguration, Feature-Flags). Redis: Für Daten, die über Serverless-Instanzen geteilt werden müssen und atomare Operationen erfordern (Rate Limits, Session-Daten). TanStack Query: Für clientseitiges Server-State-Caching mit automatischer Revalidierung.Ausfallsicheres Design
Das Rate-Limiting-System ist so ausgelegt, dass es fail-open funktioniert — wenn Redis nicht verfügbar ist, werden Anfragen mit einer Warnung durchgelassen. Dies verhindert, dass Redis-Ausfälle legitimen Benutzerverkehr blockieren:
Redis verfügbar?
|
├── Ja → Normales Rate Limiting
| (Limits prüfen, Erfolg/Fehler zurückgeben)
|
└── Nein → Fail-Open-Modus
|--- Warnung protokollieren: "Rate limiting disabled - Redis not available"
|--- Erfolg mit maximalen Limits zurückgeben
|--- Anfrage wird normal verarbeitet
Wenn Redis nicht verfügbar ist, sind alle Rate Limits deaktiviert. Das ist beabsichtigt — legitime Anfragen zu blockieren ist schlimmer als vorübergehend zusätzliche Anfragen zuzulassen. Überwache den Redis-Verbindungsstatus in der Produktion, um Ausfälle frühzeitig zu erkennen.
Dieses Muster wird einheitlich sowohl für den API-Rate-Limiter als auch für den KI-Rate-Limiter angewendet. Einzelne Redis-Aufruf-Fehler werden ebenfalls abgefangen und mit Fail-Open-Verhalten behandelt.
Debugging-Tools
Das Kit enthält zwei Skripte zum Inspizieren und Zurücksetzen von Redis-Rate-Limit-Daten:
| Skript | Befehl | Zweck |
|---|---|---|
scripts/inspect-redis-keys.ts | npx tsx scripts/inspect-redis-keys.ts | Alle Rate-Limit-Schlüssel in Redis mit Werten und TTLs auflisten |
scripts/reset-rate-limits.ts | npx tsx scripts/reset-rate-limits.ts | Alle Rate-Limit-Zähler zurücksetzen (nützlich während der Entwicklung) |
Beide Skripte verwenden dieselben Umgebungsvariablen wie deine Upstash-Redis-Instanz.
Wenn du während der Entwicklung ein Rate Limit erreichst, führe
npx tsx scripts/reset-rate-limits.ts aus, um alle Zähler zu löschen. Du kannst auch die Funktion resetAPIRateLimit() programmatisch für bestimmte Kategorien verwenden.Redis-Schlüsselmuster
Rate-Limit-Schlüssel folgen einem strukturierten Präfix-Muster zur einfachen Identifikation:
| Präfix | System | Beispiel-Schlüssel |
|---|---|---|
@upstash/ratelimit/api/{category}/{type} | API-Rate-Limiter | @upstash/ratelimit/api/upload/user:user:abc123 |
@upstash/ratelimit/ai/global | KI globaler Burst | @upstash/ratelimit/ai/global:user:abc123 |
@upstash/ratelimit/ai/{tier} | KI-Stufen-Limit | @upstash/ratelimit/ai/pro:user:abc123 |
Alle Schlüssel werden von der
@upstash/ratelimit-Bibliothek verwaltet und laufen automatisch basierend auf der Sliding-Window-Dauer ab.Umgebungsvariablen
| Variable | Erforderlich | Standard | Zweck |
|---|---|---|---|
UPSTASH_REDIS_REST_URL | Nein | — | Upstash-Redis-REST-API-URL |
UPSTASH_REDIS_REST_TOKEN | Nein | — | Upstash-Redis-REST-API-Token |
AI_RATE_LIMIT_WINDOW | Nein | 10 | Globales Burst-Zeitfenster in Sekunden |
AI_RATE_LIMIT_MAX_REQUESTS | Nein | 10 | Globale maximale Burst-Anfragen pro Zeitfenster |
AI_FREE_TIER_REQUESTS | Nein | 20 | Free-Stufe monatliches KI-Anfragen-Limit |
AI_BASIC_TIER_REQUESTS | Nein | 100 | Basic-Stufe monatliches KI-Anfragen-Limit |
AI_PRO_TIER_REQUESTS | Nein | 1000 | Pro-Stufe monatliches KI-Anfragen-Limit |
AI_ENTERPRISE_TIER_REQUESTS | Nein | 10000 | Enterprise-Stufe monatliches KI-Anfragen-Limit |
Wichtige Dateien
| Datei | Zweck |
|---|---|
apps/boilerplate/src/lib/security/api-rate-limiter.ts | Kategoriebasiertes API-Rate-Limiting (Upload, E-Mail, Kontakt usw.) |
apps/boilerplate/src/lib/security/rate-limit-middleware.ts | withRateLimit()-Middleware-Factory für API-Routen |
apps/boilerplate/src/lib/ai/rate-limiter.ts | KI-spezifisches Rate Limiting (stufenbasiert, globaler Burst) |
apps/boilerplate/scripts/inspect-redis-keys.ts | Debug-Skript zum Inspizieren von Redis-Schlüsseln und TTLs |
apps/boilerplate/scripts/reset-rate-limits.ts | Debug-Skript zum Zurücksetzen aller Rate-Limit-Zähler |