Seven Labs
Kontakt
Zurück zu allen Notizen

Redis-Caching für Next.js 15 Apps implementieren

Seven Labs
Seven Labs
·1. Juni 2026·6 min read·3,268
Next.js 15App RouterServer ComponentREDIS2ms LatencyRedis.get()Cache Hit

Redis-Caching für Next.js 15 Apps implementieren

Next.js 15 führt standardmäßig aggressive Caching-Strategien ein. Sich bei Anwendungen auf Unternehmensebene jedoch ausschließlich auf das integrierte Dateisystem-Caching oder In-Memory-Caching zu verlassen, führt unweigerlich zu veralteten Daten, unvorhersehbarer Leistung und Deployment-Problemen. Sobald Sie über mehrere serverlose Funktionen oder Container hinweg skalieren, bricht das lokale Caching zusammen. Sie benötigen einen verteilten Cache. Sie benötigen Redis.

Dieser Leitfaden beschreibt die präzise Implementierung von Redis-Caching für Next.js 15 Apps. Wir werden eine robuste, skalierbare Architektur unter Verwendung von Upstash Redis (oder einem beliebigen anderen Redis-Anbieter) und der nativen Next.js unstable_cache-API aufbauen. So umgehen wir die unzuverlässigen Voreinstellungen und übernehmen die volle Kontrolle über unsere Datenschicht.

Das Problem

Next.js 15 verlässt sich stark auf den Cache der Fetch-API und das Route-Segment-Caching. Standardmäßig speichert das Framework die gecachten Daten im Dateisystem. Das funktioniert hervorragend für eine statische Website, die auf einer einzelnen Node.js-Instanz bereitgestellt wird.

Wenn Sie jedoch auf serverlosen Plattformen wie Vercel oder AWS Lambda deployen, skaliert Ihre Anwendung, indem sie mehrere unabhängige Instanzen startet. Instanz A hat keinen Zugriff auf den Dateisystem-Cache von Instanz B. Wenn ein Benutzer Instanz A aufruft, erhält er möglicherweise aktuelle Daten. Trifft er auf Instanz B, erhält er eventuell veraltete Daten oder löst eine unnötige Datenbankabfrage aus.

Darüber hinaus ist das Dateisystem-Caching flüchtig. Das Bereitstellen einer neuen Version Ihrer App löscht oft den gesamten Cache, was unmittelbar nach einem Deployment zu massiven Traffic-Spitzen auf Ihrer Datenbank führt (ein sogenannter Cache Stampede).

Wir benötigen einen dauerhaften, verteilten Cache, der außerhalb des Anwendungscodes liegt und Deployments übersteht.

Warum es schwierig ist

Die Integration von Redis in Next.js besteht nicht nur darin, redis.get() und redis.set() aufzurufen. Next.js 15 besitzt eine sehr eigene React Server Components (RSC) Architektur. Das Framework möchte die Caching-Schicht selbst kontrollieren.

Wenn Sie Ihre Datenbankaufrufe einfach in Redis-Befehle einpacken, arbeiten Sie gegen das Framework. Sie verlieren die Vorteile der On-Demand-Revalidierung (revalidateTag, revalidatePath) und riskieren, dem Client inkonsistente Daten bereitzustellen.

Die Herausforderung besteht darin, Redis in den Caching-Lebenszyklus von Next.js zu injizieren. Wir müssen die Lese- und Schreibvorgänge des Caches abfangen, den flüchtigen Dateisystem-Cache nahtlos durch unseren verteilten Redis-Speicher ersetzen und gleichzeitig die Kompatibilität mit den Revalidierungs-Primitiven von Next.js wahren.

Dies erfordert benutzerdefinierte Cache-Handler - eine Funktion, die in Next.js in verschiedenen experimentellen Stadien vorlag. In Next.js 15 ist die Konfiguration eines benutzerdefinierten Cache-Handlers der einzig gangbare Weg für Anwendungen mit hohem Datenverkehr.

Architektur

Unsere Architektur besteht aus drei Schichten:

  1. Die Anwendungsschicht (Next.js 15): React Server Components, die die Geschäftslogik ausführen und die UI rendern.
  2. Der Cache-Interceptor: Ein benutzerdefinierter Next.js-Cache-Handler, der unstable_cache- und fetch-Anfragen abfängt.
  3. Die verteilte Caching-Schicht (Redis): Ein schneller In-Memory-Schlüssel-Wert-Speicher, der die serialisierten Antworten bereithält.

Wenn eine Server-Komponente Daten anfordert:

  1. Das Framework ruft unseren benutzerdefinierten Cache-Handler auf.
  2. Der Handler prüft Redis auf den entsprechenden Schlüssel.
  3. Bei einem Cache-Hit (Treffer) deserialisieren wir das JSON und geben es zurück. Das Framework rendert sofort die UI.
  4. Bei einem Cache-Miss (Fehlschlag) führt das Framework die Datenabruflogik aus (z. B. eine Abfrage an PostgreSQL) und übergibt das Ergebnis an unseren Handler, der es in Redis schreibt, bevor es an die Komponente zurückgegeben wird.

Dies stellt sicher, dass alle serverlosen Instanzen auf dieselbe Quelle der Wahrheit zugreifen.

Implementierung

Wir werden das Paket @neshca/cache-handler verwenden. Es bietet eine robuste, produktionsreife Basis für benutzerdefinierte Next.js-Cache-Handler, die speziell für die Anbindung von Redis entwickelt wurde. Als Redis-Client verwenden wir ioredis.

1. Abhängigkeiten installieren

bash
npm install @neshca/cache-handler ioredis

2. Den Redis-Client konfigurieren

Erstellen Sie eine robuste Redis-Client-Instanz. Initialisieren Sie nicht mehrere Verbindungen pro serverlosem Aufruf.

typescript
1// lib/redis.ts
2import { Redis } from 'ioredis';
3
4const redisUrl = process.env.REDIS_URL;
5
6if (!redisUrl) {
7  throw new Error('REDIS_URL environment variable is not defined');
8}
9
10// Einzelne Instanz in der Entwicklung sicherstellen, um Verbindungslecks während HMR zu verhindern
11const globalForRedis = global as unknown as { redis: Redis };
12
13export const redis = globalForRedis.redis || new Redis(redisUrl, {
14  maxRetriesPerRequest: 3,
15  enableReadyCheck: false,
16});
17
18if (process.env.NODE_ENV !== 'production') globalForRedis.redis = redis;

3. Den Cache-Handler erstellen

Diese Datei teilt Next.js mit, wie es mit Redis kommunizieren soll. Sie ordnet Next.js-Cache-Operationen (get, set, revalidateTag) den entsprechenden Redis-Befehlen zu.

javascript
1// cache-handler.mjs
2import { CacheHandler } from '@neshca/cache-handler';
3import createRedisHandler from '@neshca/cache-handler/redis-strings';
4import { Redis } from 'ioredis';
5
6CacheHandler.onCreation(async () => {
7  let client;
8
9  try {
10    // Wir instanziieren den Client hier.
11    // Stellen Sie in der Produktion sicher, dass REDIS_URL gesetzt ist.
12    client = new Redis(process.env.REDIS_URL, {
13      maxRetriesPerRequest: 3,
14      lazyConnect: true, // Den Start nicht blockieren
15    });
16    
17    // Verbindung testen
18    client.on('error', (error) => {
19      console.error('Redis connection error:', error);
20    });
21
22  } catch (error) {
23    console.warn('Failed to initialize Redis client for cache handler', error);
24  }
25
26  return {
27    handlers: [
28      createRedisHandler({
29        client,
30        keyPrefix: 'next-cache:',
31        timeoutMs: 1000, // Schnell abbrechen, wenn Redis langsam ist
32      }),
33    ],
34  };
35});
36
37export default CacheHandler;

4. Integration in Next.js

Weisen Sie Next.js in der next.config.js an, Ihren benutzerdefinierten Cache-Handler zu verwenden.

javascript
1// next.config.js
2/** @type {import('next').NextConfig} */
3const nextConfig = {
4  experimental: {
5    // Hinweis: Experimentelle Features können sich ändern, aber dies ist das aktuelle Muster für Next 15
6    cacheHandler: require.resolve('./cache-handler.mjs'),
7    cacheLife: {
8      default: {
9        stale: 3600, // 1 Stunde
10        revalidate: 86400, // 1 Tag
11      },
12    },
13  },
14};
15
16module.exports = nextConfig;

5. Daten abrufen

Verwenden Sie nun unstable_cache oder das native fetch wie gewohnt. Das Framework leitet das Caching transparent über Redis.

typescript
1// app/products/[id]/page.tsx
2import { unstable_cache } from 'next/cache';
3import { db } from '@/lib/db';
4
5const getProduct = unstable_cache(
6  async (id: string) => {
7    console.log('Cache miss: Fetching product from DB', id);
8    return await db.product.findUnique({ where: { id } });
9  },
10  ['product-details'], // Segmente des Cache-Schlüssels
11  { tags: ['products'], revalidate: 3600 } 
12);
13
14export default async function ProductPage({ params }: { params: { id: string } }) {
15  const product = await getProduct(params.id);
16
17  if (!product) return <div>Product not found</div>;
18
19  return (
20    <main>
21      <h1>{product.name}</h1>
22      <p>{product.description}</p>
23    </main>
24  );
25}

Wenn Sie diese Daten invalidieren müssen (z. B. über einen Webhook im Administrations-Panel), rufen Sie einfach revalidateTag('products') auf. Der Cache-Handler übersetzt dies in ein Redis DEL oder eine tag-basierte Invalidierung, was sicherstellt, dass die nächste Anfrage die Datenbank trifft.

Fallstricke

Die Implementierung von Redis-Caching in Next.js 15 birgt einige architektonische Risiken. Vermeiden Sie diese typischen Fehler.

1. Redis-Latenz und Timeout-Storms

Redis ist extrem schnell, aber Netzwerklatenzen sind unberechenbar. Wenn Ihre Redis-Instanz ausfällt oder die Verbindungslatenz auf 2000 ms ansteigt, blockiert Ihre gesamte Next.js-App beim Warten auf den Cache. Lösung: Setzen Sie in Ihrem Cache-Handler strikte Timeouts durch (z. B. timeoutMs: 500). Wenn Redis nicht innerhalb von 500 ms antwortet, muss der Cache-Handler den Fehler abfangen, dies als Cache-Miss behandeln und auf die Datenbank ausweichen. Verfügbarkeit geht vor Performance.

2. Serialisierung großer Objekte

Redis speichert Zeichenketten (Strings). Next.js serialisiert Ihre Daten in JSON, bevor sie an den Cache-Handler übergeben werden. Das Speichern riesiger 5-MB-JSON-Blobs, die eine komplette, nicht paginierte Tabelle darstellen, verstopft Ihre Redis-Netzwerk-I/O und verschlingt CPU-Zyklen für die Serialisierung und Deserialisierung. Lösung: Cachen Sie nur das, was Sie wirklich benötigen. Schränken Sie Ihre Datenbankabfragen ein. Führen Sie kein SELECT * aus. Rufen Sie nur die Felder ab, die von der Komponente benötigt werden, um die Größe der Nutzlast zu reduzieren.

3. Cache Stampedes

Wenn ein viel genutzter Cache-Schlüssel abläuft oder invalidiert wird, können Hunderte von parallelen Anfragen den Server gleichzeitig treffen. Alle erleben einen Cache-Miss und fragen gleichzeitig die Datenbank für dieselbe Abfrage ab, was Ihre Hauptdatenbank lahmlegen kann. Lösung: Das native unstable_cache von Next.js bietet bereits ein Request-Deduping pro Instanz. Für ein verteiltes Deduping benötigen Sie einen Redis-basierten Sperrmechanismus (Locking) oder setzen auf Stale-While-Revalidate-Muster (SWR), sodass der Benutzer veraltete Daten erhält, während die Revalidierung im Hintergrund stattfindet.

4. Verbindungslecks in Serverless-Umgebungen

Serverlose Umgebungen (wie Vercel-Funktionen) frieren Ausführungskontexte ein und tauen sie wieder auf. Wenn Sie bei jeder Anfrage eine neue Redis-Verbindung öffnen, schöpfen Sie Ihr Redis-Verbindungslimit sofort aus. Lösung: Instanziieren Sie den Redis-Client außerhalb des Scopes des Request-Handlers. Nutzen Sie HTTP-basierte Redis-Clients (wie die REST-API von Upstash), wenn Sie keine dauerhaften TCP-Verbindungen aufrechterhalten können, obwohl @neshca/cache-handler persistente Clients bei korrekter Konfiguration gut unterstützt.

Ergebnis

Durch die Implementierung eines benutzerdefinierten Redis-Cache-Handlers ersetzen Sie den unvorhersehbaren, flüchtigen Dateisystem-Cache durch einen robusten, zentralisierten Datenspeicher.

Ihre Deployments löschen den Cache nicht mehr. Ihre serverlosen Instanzen greifen auf eine einzige Quelle der Wahrheit zu. Die Last auf Ihre Datenbank sinkt drastisch und Ihre Antwortzeiten stabilisieren sich über das gesamte Cluster hinweg.

Next.js 15 möchte das Caching selbst verwalten. Lassen Sie es die API kontrollieren. Aber Sie müssen die Infrastruktur kontrollieren. Redis gibt Ihnen die nötige Kontrolle, um Next.js im großen Maßstab zu betreiben. Geben Sie sich nicht mit den Standards zufrieden.

Seven Labs Dienstleistung

SaaS-Entwicklung - Next.js & MERN

Wir entwickeln Next.js-Anwendungen. Siehe unsere SaaS-Dienste →
Loading...
Chat with us
Book a Call
Free · 30 min · No commitment

Book a Strategy Call

30 minutes. No sales pitch. We scope your project and tell you honestly if we're the right fit.