12k
All articles

Datenabruf in React Server Components

Daten in React Server Components mit async-Funktionen laden, das Cache-Verhalten in Next.js 15/16 verstehen und Waterfalls mit Promise.all vermeiden.

OpenReplay Team
OpenReplay Team
Datenabruf in React Server Components

In einer React Server Component rufen Sie Daten ab, indem Sie die Komponente zu einer async-Funktion machen und den Request direkt im Funktionskörper await-en — kein useEffect, kein Ladezustand und kein clientseitiger API-Roundtrip. Dies ist das Standardmodell im Next.js App Router und kehrt zwei Gewohnheiten aus dem klassischen Client-React um: Der Datenabruf verlagert sich in das Rendering, und die größte Fehlerquelle ist das Caching, das in Next.js 15 seine Richtung geändert hat. Dieser Artikel behandelt das aktuelle Muster, die Caching-Realität in Next.js 16, warum "use server" nichts mit Server Components zu tun hat, und wie Sie Request-Wasserfälle vermeiden.

Wesentliche Erkenntnisse

  • In einer Server Component rufen Sie Daten ab, indem Sie die Komponente async machen und den Request awaiten. Da Server Components auf dem Server gerendert werden, sind Zugangsdaten und Abfragelogik nicht im Client-Bundle enthalten, sodass Sie eine Datenbank direkt mit einem ORM abfragen können.
  • Standardmäßig werden fetch-Anfragen im aktuellen Next.js nicht gecacht — das Gegenteil des Verhaltens in Next.js 13/14, das viele Tutorials noch beschreiben.
  • Für Caching müssen Sie sich explizit entscheiden: { cache: 'force-cache' } oder { next: { revalidate } } im vorherigen Modell, oder die use cache-Direktive, sobald cacheComponents: true gesetzt ist.
  • Es gibt keine Direktive für Server Components; die „use server”-Direktive wird für Server Functions (Server Actions) verwendet — ein separates Feature.
  • Starten Sie unabhängige Fetches ohne await und lösen Sie sie mit Promise.all auf, um einen sequenziellen Wasserfall zu vermeiden.

Wie ruft man Daten in einer Server Component ab?

Das Kernmuster besteht aus einem einzigen Schritt: Machen Sie die Komponente zu einer async-Funktion und awaiten Sie den Request. Server Components sind der Standard für Layouts und Pages im App Router, daher ist keine Direktive erforderlich, um dieses Verhalten zu erhalten — async-Komponenten sind ein Feature von Server Components, das es erlaubt, im Render zu awaiten.

// app/blog/page.tsx
export default async function Page() {
  const res = await fetch('https://api.vercel.app/blog')
  const posts = await res.json()
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

Da der Code ausschließlich auf dem Server ausgeführt wird, können Sie die API-Schicht überspringen und Ihre Datenquelle direkt abfragen. Sie können Datenbankabfragen sicher mit einem ORM oder einem Datenbank-Client durchführen, sollten jedoch sicherstellen, dass Anfragen ordnungsgemäß authentifiziert und autorisiert sind.

// app/blog/page.tsx
import { db, posts } from '@/lib/db'

export default async function Page() {
  const allPosts = await db.select().from(posts)
  return (
    <ul>
      {allPosts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

Vergleichen Sie dies mit dem Client-Muster, das Sie ersetzen: Datenabruf in einem useEffect, Speicherung des Ergebnisses in useState und Auslieferung der Fetch-Logik (einschließlich etwaiger Secrets) an den Browser. Dieser Ansatz verursacht einen zusätzlichen Client-Server-Roundtrip nach dem Laden der Seite; das Awaiten auf dem Server entfällt dieser Umweg.

fetch wird standardmäßig nicht gecacht — die Änderung in Next.js 15/16

Im aktuellen Next.js wird fetch standardmäßig nicht gecacht — das ist es wert, klar festzuhalten, da die meisten älteren Inhalte das Gegenteil behaupten. Laut der aktuellen Next.js fetch-Referenz ist der Standard auto no cache: Next.js ruft die Ressource bei jeder Anfrage vom Remote-Server ab. Das vorherige Caching-Modell verhielt sich ebenso — standardmäßig werden fetch-Anfragen nicht gecacht, und Sie cachen eine einzelne Anfrage, indem Sie die cache-Option auf 'force-cache' setzen. In Next.js 13/14 wurde ein unqualifiziertes fetch standardmäßig gecacht (force-cache), sodass jedes Tutorial, das sich darauf stützt, veraltet ist.

Um unter dem vorherigen Modell zu cachen, aktivieren Sie dies pro Anfrage:

// Cachen bis zur manuellen Revalidierung
await fetch('https://api.example.com/posts', { cache: 'force-cache' })

// Gecachte Daten ausliefern, maximal alle 3600 Sekunden revalidieren
await fetch('https://api.example.com/posts', { next: { revalidate: 3600 } })

Next.js 16 liefert außerdem ein zweites Modell, Cache Components, dessen Aktivierung die use cache-Direktive ist. Der Stolperstein, der viele verwirrt: use cache ist ein Feature von Cache Components; um es zu aktivieren, fügen Sie die cacheComponents-Option in Ihre next.config.ts-Datei ein. Ohne dieses Flag tut use cache nichts, und Sie fallen auf die oben genannten fetch-Optionen zurück.

// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { cacheComponents: true }
export default nextConfig
import { cacheLife } from 'next/cache'

export async function getPosts() {
  'use cache'
  cacheLife('hours')
  const res = await fetch('https://api.example.com/posts')
  return res.json()
}

Hier cacht die use cache-Direktive den Rückgabewert von async-Funktionen und Komponenten, und cacheLife() legt die Dauer fest. Beachten Sie, dass unstable_cache in Next.js 16 durch die use cache-Direktive ersetzt wird — verwenden Sie es nicht mehr in neuem Code.

Cache Components AUS (vorheriges Modell)Cache Components AN (cacheComponents: true)
fetch-StandardNicht gecachtNicht gecacht
Fetch cachen{ cache: 'force-cache' }use cache-Direktive
Zeitbasierte Revalidierung{ next: { revalidate: n } }cacheLife()-Profil
Nicht-fetch-Daten cachenReact.cache / Route-Konfigurationuse cache auf der Funktion

Zwei Verhaltensweisen sind unabhängig vom Caching. Erstens werden fetch-Anfragen mit GET, die dieselbe URL und dieselben Optionen verwenden, während eines Server-Render-Durchlaufs automatisch memoized — wenn Sie denselben Fetch in mehreren Komponenten aufrufen, führt Next.js ihn einmal aus und teilt das Ergebnis. Zweitens: Für Nicht-fetch-Quellen kapseln Sie den Aufruf in Reacts cache(); React.cache ist auf die aktuelle Anfrage beschränkt — jede Anfrage erhält ihren eigenen Memoization-Scope ohne Weitergabe zwischen Anfragen.

„use server” macht keine Server Component

Wenn etwas RSC-Einsteiger aus der Bahn wirft, dann ist es diese Direktive. Ein weit verbreitetes Missverständnis ist, dass Server Components durch "use server" gekennzeichnet werden, aber es gibt keine Direktive für Server Components; die "use server"-Direktive wird für Server Functions verwendet. Server Components sind schlicht der Standard im App Router — eine Datei ohne Direktive ist bereits eine. "use server" markiert Server Functions (Server Actions), und laut der React-Referenz sind Server Functions für Mutationen konzipiert, die serverseitigen Zustand aktualisieren; sie werden nicht für den Datenabruf empfohlen. Verwenden Sie einfache async Server Components zum Lesen von Daten und "use server" zum Schreiben.

Wasserfälle vermeiden und langsame Daten streamen

Das sequenzielle Awaiten von Anfragen erzeugt einen Wasserfall. Innerhalb einer Komponente können mehrere async/await-Anfragen immer noch sequenziell sein, wenn sie nacheinander platziert werden; starten Sie mehrere Anfragen durch Aufruf von fetch und awaiten Sie sie dann mit Promise.all. Das Aufrufen der Funktionen ohne await startet sie sofort:

export default async function Page({ params }: { params: Promise<{ username: string }> }) {
  const { username } = await params
  const artistData = getArtist(username)   // startet sofort
  const albumsData = getAlbums(username)   // startet sofort
  const [artist, albums] = await Promise.all([artistData, albumsData])
  return <h1>{artist.name}</h1>
}

Bei genuinen langsamen Daten sollten Sie die Route nicht blockieren. Wenn Sie langsame Datenanfragen haben, wird die gesamte Route am Rendern gehindert, bis alle Daten abgerufen sind; um die Ladezeit zu verbessern, unterteilen Sie die Seite in Abschnitte und senden Sie diese progressiv an den Client. Kapseln Sie den langsamen Teilbaum in <Suspense> (oder fügen Sie eine loading.js hinzu), und für Daten mit niedrigerer Priorität starten Sie das Promise auf dem Server und lesen es auf dem Client mit der use-API:

// Server Component — Hinweis: commentsPromise wird NICHT geawaitet
import { Suspense } from 'react'
import Comments from './comments'

export default async function Page({ id }: { id: string }) {
  const commentsPromise = getComments(id)
  return (
    <Suspense fallback={<p>Kommentare werden geladen…</p>}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  )
}
// Client Component
'use client'
import { use } from 'react'

export default function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
  const comments = use(commentsPromise)
  return comments.map((c) => <p key={c.id}>{c.text}</p>)
}

Sie starten das Promise auf dem Server und warten auf dem Client mit der use-API darauf; da async-Komponenten auf dem Client nicht unterstützt werden, awaiten Sie das Promise mit use. Wenn dabei etwas schiefläuft, ist der Fehler nur dort sichtbar, wo sich der Nutzer befindet — ein Suspense-Fallback, der sich nie in Inhalt auflöst, oder ein gestreamter Chunk, der nach der Hydration ankommt — und eine Session-Replay-Aufzeichnung ermöglicht es Ihnen, genau nachzuvollziehen, was der Browser in diesem Moment gerendert hat.

Secrets auf der Serverseite der Grenze halten

Auf dem Server abgerufene Daten gelangen als Props zu Client Components, und diese Props müssen serialisierbar sein — übergeben Sie einfache Daten, keine Funktionen oder Klasseninstanzen. Secrets bleiben standardmäßig geschützt, da Server Component-Code nie ausgeliefert wird, aber gemeinsam genutzte Module können Lecks verursachen. In Next.js werden nur Umgebungsvariablen mit dem Präfix NEXT_PUBLIC_ in das Client-Bundle aufgenommen; Variablen ohne dieses Präfix werden von Next.js durch einen leeren String ersetzt. Um ein Modul mit Secrets abzusichern, importieren Sie das server-only-Paket an dessen Anfang, sodass ein versehentlicher Client-Import beim Build-Vorgang fehlschlägt. React Context kann ebenfalls nicht in einer Server Component leben — kapseln Sie ihn in einem 'use client'-Provider und rendern Sie diesen Provider innerhalb Ihres Server-Layouts.

Der gedankliche Wandel ist klein, aber vollständig: Verlagern Sie den Fetch in eine async-Komponente, awaiten Sie ihn, und betrachten Sie Caching als etwas, für das Sie sich aktiv entscheiden müssen, anstatt als etwas, das automatisch passiert. Beginnen Sie damit, einen useEffect-Datenabruf in eine async Server Component zu portieren, bestätigen Sie, dass die Anfrage serverseitig ausgeführt wird, und entscheiden Sie dann pro Route, ob diese Daten mit { next: { revalidate } } oder der use cache-Direktive gecacht werden sollen.

Häufig gestellte Fragen

Wird fetch in Next.js 16 standardmäßig gecacht?

Nein. Ab Next.js 15 und weiterhin in Version 16 wird fetch standardmäßig nicht gecacht und trifft bei jeder Anfrage den Ursprungsserver. Dies ist das Gegenteil von Next.js 13/14, wo ein unqualifiziertes fetch standardmäßig mit force-cache gecacht wurde. Um unter dem vorherigen Modell zu cachen, aktivieren Sie dies pro Anfrage, indem Sie cache auf force-cache setzen oder einen next revalidate-Wert angeben; ältere Tutorials, die von standardmäßigem Caching ausgehen, sind veraltet.

Was ist der Unterschied zwischen use cache und next revalidate beim Cachen eines fetch?

Sie gehören zu zwei verschiedenen Modellen. Die next revalidate-Option und force-cache funktionieren im vorherigen, standardmäßigen Caching-Modell ohne zusätzliche Konfiguration. Die use cache-Direktive ist ein Cache Components-Feature, das nur wirksam wird, wenn cacheComponents in next.config.ts auf true gesetzt ist, und dessen Dauer mit cacheLife gesteuert wird. Ohne dieses Flag tut use cache nichts, sodass ein fetch, den Sie cachen wollten, stillschweigend bei jeder Anfrage den Ursprungsserver trifft.

Kann ich useEffect zum Datenabruf in einer Server Component verwenden?

Nein. useEffect wird nur im Browser ausgeführt und kann daher nicht innerhalb einer Server Component ausgeführt werden, die ausschließlich auf dem Server gerendert wird. In einer Server Component machen Sie die Komponente async und awaiten den Request direkt im Render, ohne Ladezustand und ohne Client-Roundtrip. Wenn Sie clientseitigen Datenabruf benötigen, fügen Sie die use client-Direktive hinzu, um daraus eine Client Component zu machen, oder starten Sie das Promise auf dem Server und lesen Sie es auf dem Client mit Reacts use-API.

Macht die use server-Direktive eine Komponente zur Server Component?

Nein. Es gibt keine Direktive für Server Components; sie sind schlicht der Standard im App Router, sodass eine Datei ohne Direktive bereits eine ist. Die use server-Direktive markiert Server Functions, auch Server Actions genannt, die für Mutationen konzipiert sind, die serverseitigen Zustand aktualisieren, und nicht für den Datenabruf empfohlen werden. Verwenden Sie eine einfache async Server Component zum Lesen von Daten und use server zum Schreiben.

Was passiert, wenn ein fetch fehlschlägt, wenn Promise.all für parallele Anfragen verwendet wird?

Promise.all lehnt ab, sobald eine einzelne Anfrage abgelehnt wird, verwirft die Ergebnisse der anderen und lässt das gesamte Rendering fehlschlagen. Wenn eine Anfrage fehlschlagen darf, ohne die übrigen zu beeinträchtigen, verwenden Sie stattdessen Promise.allSettled, das mit dem Status jeder Anfrage auflöst und es Ihnen ermöglicht, Fehler individuell zu behandeln. Starten Sie die Fetches ohne await, um sie parallel auszuführen, und wählen Sie dann Promise.all oder Promise.allSettled je nachdem, wie Sie mit Fehlern umgehen möchten.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.