Caching & Redis

Upstash Redis für API-Rate-Limiting und Caching — kategoriebasierte Limits, KI-Kontingente und ausfallsicheres Design

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

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

KategorieBenutzer-LimitIP-LimitAnwendungsfall
upload10 Req/Stunde20 Req/StundeDatei-Upload-Endpunkt
email5 Req/Stunde10 Req/StundeE-Mail-Versand-Endpunkte
contact3 Req/StundeKontaktformular (nur IP, keine Auth erforderlich)
payments20 Req/StundeZahlungs- und Checkout-Endpunkte
webhooks100 Req/StundeWebhook-Verarbeitung (hoher Durchsatz)
api100 Req/Stunde200 Req/StundeAllgemeine 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:
  1. Extrahiert die Benutzer-ID aus der Clerk-Authentifizierung
  2. Extrahiert die Client-IP aus x-forwarded-for- oder x-real-ip-Headern
  3. Prüft sowohl Benutzer- als auch IP-Rate-Limits für die Kategorie
  4. Gibt 429 Too Many Requests mit Retry-Informationen zurück, wenn überschritten
  5. 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

StufeMonatliche AnfragenZeitfensterOverride-Variable
Free2030 TageAI_FREE_TIER_REQUESTS
Basic10030 TageAI_BASIC_TIER_REQUESTS
Pro1.00030 TageAI_PRO_TIER_REQUESTS
Enterprise10.00030 TageAI_ENTERPRISE_TIER_REQUESTS

Dreistufige Prüfung

Jede KI-Anfrage durchläuft drei aufeinander folgende Prüfungen:
  1. Globaler Burst-Schutz — 10 Anfragen pro 10 Sekunden (konfigurierbar über AI_RATE_LIMIT_MAX_REQUESTS und AI_RATE_LIMIT_WINDOW). Dies verhindert, dass ein einzelner Benutzer die KI-Endpunkte überflutet.
  2. 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.
  3. 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:
HeaderWertBeschreibung
X-RateLimit-Limit100Maximale Anfragen im Zeitfenster
X-RateLimit-Remaining87Verbleibende Anfragen im aktuellen Zeitfenster
X-RateLimit-Reset1708012800000Unix-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.

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
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:
SkriptBefehlZweck
scripts/inspect-redis-keys.tsnpx tsx scripts/inspect-redis-keys.tsAlle Rate-Limit-Schlüssel in Redis mit Werten und TTLs auflisten
scripts/reset-rate-limits.tsnpx tsx scripts/reset-rate-limits.tsAlle Rate-Limit-Zähler zurücksetzen (nützlich während der Entwicklung)
Beide Skripte verwenden dieselben Umgebungsvariablen wie deine Upstash-Redis-Instanz.

Redis-Schlüsselmuster

Rate-Limit-Schlüssel folgen einem strukturierten Präfix-Muster zur einfachen Identifikation:
PräfixSystemBeispiel-Schlüssel
@upstash/ratelimit/api/{category}/{type}API-Rate-Limiter@upstash/ratelimit/api/upload/user:user:abc123
@upstash/ratelimit/ai/globalKI 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

VariableErforderlichStandardZweck
UPSTASH_REDIS_REST_URLNeinUpstash-Redis-REST-API-URL
UPSTASH_REDIS_REST_TOKENNeinUpstash-Redis-REST-API-Token
AI_RATE_LIMIT_WINDOWNein10Globales Burst-Zeitfenster in Sekunden
AI_RATE_LIMIT_MAX_REQUESTSNein10Globale maximale Burst-Anfragen pro Zeitfenster
AI_FREE_TIER_REQUESTSNein20Free-Stufe monatliches KI-Anfragen-Limit
AI_BASIC_TIER_REQUESTSNein100Basic-Stufe monatliches KI-Anfragen-Limit
AI_PRO_TIER_REQUESTSNein1000Pro-Stufe monatliches KI-Anfragen-Limit
AI_ENTERPRISE_TIER_REQUESTSNein10000Enterprise-Stufe monatliches KI-Anfragen-Limit

Wichtige Dateien

DateiZweck
apps/boilerplate/src/lib/security/api-rate-limiter.tsKategoriebasiertes API-Rate-Limiting (Upload, E-Mail, Kontakt usw.)
apps/boilerplate/src/lib/security/rate-limit-middleware.tswithRateLimit()-Middleware-Factory für API-Routen
apps/boilerplate/src/lib/ai/rate-limiter.tsKI-spezifisches Rate Limiting (stufenbasiert, globaler Burst)
apps/boilerplate/scripts/inspect-redis-keys.tsDebug-Skript zum Inspizieren von Redis-Schlüsseln und TTLs
apps/boilerplate/scripts/reset-rate-limits.tsDebug-Skript zum Zurücksetzen aller Rate-Limit-Zähler