Appearance
Kapitel 7: Datenabfrage
📖 Lernziele
In diesem Kapitel lernen Sie:
- ✅ Daten auf Server Components abfragen (async/await)
- ✅ Caching-Strategien verstehen
- ✅ Daten auf Client Components abfragen (useEffect, SWR)
- ✅ API-Anfragen kapseln (Request Wrapper)
- ✅ CORS-Probleme lösen
- ✅ Statische Generierung (SSG), SSR und ISR verstehen
7.1 Server-Side Datenabfrage (Empfohlen)
🎯 Datenabfrage in Server Components
Vorteile:
- ✅ Keine API-Schicht nötig (direkter DB-Zugriff möglich)
- ✅ Bessere Performance (weniger Client-JS)
- ✅ Bessere SEO (Daten sind im HTML)
📝 Beispiel: Einfache Datenabfrage
Datei: app/blog/page.js (Server Component)
jsx
// Asynchrone Server Component
async function getPosts() {
const res = await fetch('https://api.example.com/posts');
if (!res.ok) throw new Error('Fehler beim Laden');
return res.json();
}
export default async function BlogPage() {
const posts = await getPosts();
return (
<main>
<h1>Blog-Artikel</h1>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
);
}📦 Caching-Strategien
Next.js bietet verschiedene Caching-Optionen:
| Option | Beschreibung | Verwendung |
|---|---|---|
| Standard | Daten werden gecached (Standard) | fetch(url) |
| Kein Cache | Daten immer neu abfragen | fetch(url, { cache: 'no-store' }) |
| Revalidierung | Cache nach X Sekunden erneuern | fetch(url, { next: { revalidate: 60 } }) |
Beispiele:
javascript
// 1. Standard (gecacht)
const res = await fetch('https://api.example.com/posts');
// 2. Kein Cache (immer aktuell)
const res = await fetch('https://api.example.com/posts', { cache: 'no-store' });
// 3. Revalidierung (alle 60 Sekunden)
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 }
});7.2 Client-Side Datenabfrage
🎯 Wann Client-Side?
- ✅ Bei interaktiven Komponenten (die Hooks verwenden)
- ✅ Wenn Daten erst nach Benutzerinteraktion geladen werden sollen
- ✅ Für private Daten (die nicht SEO-relevant sind)
📝 Methode 1: useEffect + fetch
Datei: components/PostList.js (Client Component)
jsx
'use client';
import { useState, useEffect } from 'react';
export default function PostList() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function loadPosts() {
try {
const res = await fetch('https://api.example.com/posts');
if (!res.ok) throw new Error('Fehler');
const data = await res.json();
setPosts(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
loadPosts();
}, []);
if (loading) return <p>Lädt...</p>;
if (error) return <p>Fehler: {error}</p>;
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}📝 Methode 2: SWR (Empfohlen)
Installation:
bash
pnpm add swrVerwendung:
jsx
'use client';
import useSWR from 'swr';
const fetcher = (url) => fetch(url).then(res => res.json());
export default function PostList() {
const { data, error, isLoading } = useSWR('https://api.example.com/posts', fetcher);
if (isLoading) return <p>Lädt...</p>;
if (error) return <p>Fehler: {error.message}</p>;
return (
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}Vorteile von SWR:
- ✅ Automatisches Caching
- ✅ Automatisches Revalidierung (wenn Fokus zurückkehrt)
- ✅ Fehlerbehandlung integriert
7.3 API-Anfragen kapseln
🎯 Warum kapseln?
- ✅ Einheitliche Fehlerbehandlung
- ✅ Basis-URL zentral verwalten
- ✅ Token/Auth-Header automatisch hinzufügen
📝 Beispiel: API-Client erstellen
Datei: lib/api.js
javascript
const BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'https://api.example.com';
async function request(endpoint, options = {}) {
const url = `${BASE_URL}${endpoint}`;
const defaultOptions = {
headers: {
'Content-Type': 'application/json',
// Token hinzufügen (falls vorhanden)
...(localStorage.getItem('token') && {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}),
},
};
const res = await fetch(url, { ...defaultOptions, ...options });
if (!res.ok) {
const error = await res.json();
throw new Error(error.message || 'API-Fehler');
}
return res.json();
}
export const api = {
get: (endpoint) => request(endpoint),
post: (endpoint, data) => request(endpoint, {
method: 'POST',
body: JSON.stringify(data),
}),
put: (endpoint, data) => request(endpoint, {
method: 'PUT',
body: JSON.stringify(data),
}),
delete: (endpoint) => request(endpoint, { method: 'DELETE' }),
};📝 Verwendung
jsx
import { api } from '@/lib/api';
// In einer Server Component
const posts = await api.get('/posts');
// In einer Client Component
useEffect(() => {
api.get('/posts').then(setPosts);
}, []);7.4 CORS-Probleme lösen
🎯 Was ist CORS?
CORS (Cross-Origin Resource Sharing) verhindert Anfragen an eine andere Domain aus Sicherheitsgründen.
🛠️ Lösung 1: Next.js Rewrite (Proxy)
Datei: next.config.js
javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://api.example.com/:path*',
},
];
},
};
module.exports = nextConfig;Verwendung:
javascript
// Anfrage an /api/posts wird zu https://api.example.com/posts weitergeleitet
const res = await fetch('/api/posts');🛠️ Lösung 2: API Route als Proxy
Datei: app/api/proxy/route.js
javascript
export async function GET(request) {
const url = new URL(request.url);
const endpoint = url.searchParams.get('endpoint');
const res = await fetch(`https://api.example.com/${endpoint}`, {
headers: {
'Authorization': `Bearer ${process.env.API_TOKEN}`,
},
});
const data = await res.json();
return Response.json(data);
}7.5 Rendering-Modi (SSG, SSR, ISR)
🎯 Drei Rendering-Modi
| Modus | Beschreibung | Verwendung |
|---|---|---|
| SSG (Static Site Generation) | HTML wird zur Build-Zeit generiert | Blog, Dokumentation |
| SSR (Server-Side Rendering) | HTML wird bei jeder Anfrage generiert | E-Commerce, Suche |
| ISR (Incremental Static Regeneration) | SSG + automatische Aktualisierung | Blog mit regelmäßigen Updates |
📝 SSG (Static Site Generation)
Standardverhalten (fetch gecacht):
jsx
export default async function Page() {
// Wird zur Build-Zeit ausgeführt (SSG)
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return <PostList posts={posts} />;
}📝 SSR (Server-Side Rendering)
Kein Cache (jede Anfrage neu):
jsx
export default async function Page() {
// Wird bei jeder Anfrage ausgeführt (SSR)
const res = await fetch('https://api.example.com/posts', { cache: 'no-store' });
const posts = await res.json();
return <PostList posts={posts} />;
}📝 ISR (Incremental Static Regeneration)
Revalidierung nach X Sekunden:
jsx
export default async function Page() {
// SSG, aber alle 60 Sekunden aktualisieren
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 }
});
const posts = await res.json();
return <PostList posts={posts} />;
}📝 Dynamische Routen mit generateStaticParams
Datei: app/blog/[slug]/page.js
jsx
// Statische Generierung für dynamische Routen
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then(res => res.json());
return posts.map(post => ({
slug: post.slug,
}));
}
export default async function BlogPost({ params }) {
const post = await fetch(`https://api.example.com/posts/${params.slug}`).then(res => res.json());
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}📝 Zusammenfassung
In diesem Kapitel haben Sie gelernt:
| Konzept | Erklärung |
|---|---|
| Server-Side | Datenabfrage in Server Components (async/await) |
| Client-Side | useEffect + fetch oder SWR |
| API-Kapselung | Einheitliche Fehlerbehandlung, Basis-URL |
| CORS | Lösung über Rewrites oder API-Routen |
| SSG/SSR/ISR | Verschiedene Rendering-Modi für verschiedene Anwendungsfälle |
✅ Nächste Schritte
- ✅ Übung: Erstellen Sie eine Server Component, die Daten von einer API lädt
- ✅ Übung: Erstellen Sie eine Client Component mit SWR
- ✅ Weiter geht's: Kapitel 8 - State Management
🎯 Selbsttest
Frage 1: Was ist der Vorteil von Server-Side Datenabfrage?
Antwort anzeigen
Bessere SEO, da Daten im HTML enthalten sind; bessere Performance, da weniger Client-JS benötigt wird.Frage 2: Was ist der Unterschied zwischen SSG und SSR?
Antwort anzeigen
SSG generiert HTML zur Build-Zeit (schneller), SSR generiert HTML bei jeder Anfrage (immer aktuell).Frage 3: Wie löst man CORS-Probleme in Next.js?
Antwort anzeigen
Über Rewrites in `next.config.js` oder durch Verwendung von API-Routen als Proxy.🚀 Weiter zu Kapitel 8: State Management
