3. Modern Web Development mit React¶
Dieses Kapitel beschäftigt sich mit modernen Web-Entwicklungstechnologien, wobei der Fokus auf React und der Kommunikation zwischen Frontend und Backend liegt. Wir betrachten die Grundlagen von React, State-Management, Backend-Technologien sowie typsichere API-Architekturen.
3.1. React Allgemein¶
React ist eine JavaScript-Bibliothek, mit der man moderne Benutzeroberflächen (UIs) bauen kann. Sie wurde von Facebook (heute Meta) entwickelt und arbeitet mit sogenannten Komponenten – das sind wiederverwendbare Bausteine für die UI.
Hauptideen von React:
Komponentenbasiert: Die Oberfläche besteht aus kleinen, eigenständigen Teilen (Komponenten).
Deklarativ: Man beschreibt, wie die UI aussehen soll – React kümmert sich um das Aktualisieren.
Virtuelles DOM: Änderungen an der UI werden erst im Hintergrund geprüft, bevor sie im echten Browser sichtbar werden – das macht React schnell.
JSX: Eine besondere Schreibweise, mit der man HTML in JavaScript schreiben kann.
3.1.1. Grundkonzepte von React¶
Komponenten (Components)
Komponenten sind die Bausteine in React. Sie können wie Funktionen oder Klassen aussehen:
Funktionskomponenten: Heute die übliche, einfachere Form. Sie nutzen sogenannte Hooks.
Klassenkomponenten: Früher Standard, mit speziellen Methoden für den Lebenszyklus.
Props (Properties)
Props (kurz für „Properties“) sind Daten, die von einer übergeordneten Komponente an eine andere weitergegeben werden:
Sie ändern sich nicht (sind „unveränderlich“).
Damit können Komponenten flexibel angepasst werden.
State (Zustand)
Der State beschreibt den aktuellen Zustand einer Komponente, z. B. ob ein Menü offen oder geschlossen ist:
Wird mit useState (bei Funktionen) oder this.state (bei Klassen) verwaltet.
Wenn sich der State ändert, wird die Komponente neu gerendert.
Lifecycle (Lebenszyklus)
Jede Komponente durchläuft mehrere Phasen:
Einfügen (Mounting): Komponente wird erstellt und in DOM eingefügt
Aktualisieren (Updating): Komponente wird aktualisiert (durch Props oder State)
Entfernen (Unmounting): Komponente wird aus DOM entfernt
In Funktionskomponenten nutzt man dafür den useEffect-Hook.
Hooks
Hooks sind spezielle Funktionen für Funktionskomponenten:
useState: Zustände verwalten
useEffect: Dinge tun wie z. B. Daten laden
useContext: Gemeinsame Daten verwenden
useRef: Auf DOM-Elemente zugreifen
Einfaches Beispiel einer React-Komponente:
function Welcome(props) {
return <h1>Hallo, {props.name}</h1>;
}
export default function App() {
return (
<div>
<Welcome name="Max" />
<Welcome name="Sarah" />
<Welcome name="Tom" />
</div>
);
}
3.2. React State¶
In modernen React-Anwendungen ist das State Management ein zentrales Konzept. Der State (Zustand) beschreibt, wie sich Daten innerhalb einer Komponente im Laufe der Zeit verändern. In dem react-fullstack-Projekt wird der State genutzt, um Benutzerinteraktionen, API-Daten und UI-Zustände effizient zu verwalten.
3.2.1. Was ist State in React?¶
Der State ist ein JavaScript-Objekt, das von einer Komponente verwaltet wird. Änderungen am State führen dazu, dass die Komponente (und ihre Kindkomponenten) neu gerendert werden.
3.2.2. Lokaler State (Local State)¶
Verwaltet mit:
useState,useReducerZweck: Speicherung von Daten innerhalb einer Komponente.
Beispiel: Formularfelder, Modals, UI-Toggles.
3.2.3. Globaler State (Global State)¶
Verwaltet mit: Context API, Redux, Zustand, Recoil, Jotai etc.
Zweck: Gemeinsame Nutzung von Daten zwischen mehreren Komponenten.
Beispiel: Authentifizierungsstatus, Theme, Benutzerdaten.
3.2.4. Server State (Asynchroner Remote State)¶
Verwaltet mit: SWR, React Query, Apollo Client etc.
Zweck: Synchronisation von UI und Backend-Daten.
Beispiel: API-Antworten, Caching, Hintergrund-Aktualisierung.
3.2.5. URL State¶
Verwaltet mit: React Router, Next.js router, window.location
Zweck: Zustand, der in der URL kodiert ist (z. B. Query-Parameter).
Beispiel: Pagination, Filter, Tabs.
Beispiel: Einfache Komponente mit State
import React, { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Aktueller Wert: {count}</p>
<button onClick={() => setCount(count + 1)}>Erhöhen</button>
</div>
);
}
In diesem Beispiel wird der State-Wert count mit dem React-Hook useState verwaltet. Mit jedem Klick auf den Button wird der Wert erhöht und die Komponente neu gerendert.
3.2.6. State in komplexeren Anwendungen¶
In größeren Projekten wie react-fullstack wird State häufig genutzt, um:
Daten von einer API zu laden und darzustellen
Formulareingaben zu verwalten
UI-Zustände wie Ladeindikatoren oder Fehlermeldungen zu steuern
Beispiel: Laden von Daten mit State
import React, { useState, useEffect } from "react";
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/users")
.then((res) => res.json())
.then((data) => {
setUsers(data);
setLoading(false);
});
}, []);
if (loading) return <div>Lade Benutzer...</div>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
Hier wird der State genutzt, um sowohl die geladenen Benutzerdaten als auch den Ladezustand zu verwalten.
3.2.7. State-Lifting und globale State-Verwaltung¶
Oft müssen mehrere Komponenten auf denselben State zugreifen. In solchen Fällen wird der State nach oben („lifting state up“) in eine gemeinsame Elternkomponente verschoben.
Für sehr große Anwendungen kann auch ein globales State-Management-Tool wie Redux oder Context API verwendet werden.
Beispiel: State-Lifting
function Parent() {
const [value, setValue] = useState("");
return (
<>
<Input value={value} setValue={setValue} />
<Display value={value} />
</>
);
}
function Input({ value, setValue }) {
return <input value={value} onChange={e => setValue(e.target.value)} />;
}
function Display({ value }) {
return <div>Aktueller Wert: {value}</div>;
}
Das State Management ist ein zentrales Element jeder React-Anwendung. Mit Hooks wie useState und useEffect lassen sich sowohl einfache als auch komplexe Zustände effizient verwalten. In deinem react-fullstack-Projekt sorgt ein durchdachtes State Management für eine reaktive, benutzerfreundliche Oberfläche und eine klare Datenflussstruktur.
3.3. Hono.js HTTP Server¶
3.3.1. Einführung¶
Hono ist ein minimalistisches, schnelles und modernes Web-Framework für JavaScript/TypeScript, das auf der Web-Standard-API (wie Fetch API, Request und Response) basiert. Es eignet sich hervorragend für serverlose Plattformen, Edge-Umgebungen sowie klassische Node.js-Anwendungen.
3.3.2. Architektur¶
Die Architektur von Hono ist bewusst schlank gehalten und orientiert sich stark an der Standard-Webplattform. Die wichtigsten Bestandteile sind:
Router: Zum Definieren von Routen mittels HTTP-Methoden (GET, POST, etc.).
Middleware: Zur Erweiterung oder Veränderung des Request/Response-Flows.
Kontext-Objekt (`Context`): Bietet einfachen Zugriff auf Request, Response, Parameter und Hilfsfunktionen.
Kompatibilität: Unterstützt Edge-Plattformen (Cloudflare Workers, Deno, Bun) sowie Node.js.
3.3.3. Funktionen¶
Routing: Unterstützt Routen mit Parametern, Wildcards und verschachtelten Gruppen.
Middleware-Support: Unterstützt beliebige Middleware für Logging, Authentifizierung, etc.
Standardisierte API: Verwendet die native Request und Response API.
Edge-Optimierung: Sehr kleine Bundle-Größe und extrem schnelle Startzeit.
Typisierung: Volle TypeScript-Unterstützung.
3.3.4. Beispiel: Einfache HTTP-Anwendung¶
Das folgende Beispiel zeigt eine einfache Hono-Anwendung, die HTTP-Anfragen verarbeitet:
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hallo Welt')
})
app.get('/user/:name', (c) => {
const name = c.req.param('name')
return c.text(`Hallo, ${name}`)
})
app.post('/echo', async (c) => {
const body = await c.req.text()
return c.text(`Du hast gesendet: ${body}`)
})
export default app
3.3.5. Beispielanwendungen¶
Hono eignet sich besonders gut für folgende Szenarien:
Serverlose APIs: Bereitstellung von REST-APIs auf Plattformen wie Cloudflare Workers oder Vercel Edge Functions.
Microservices: Aufbau kleiner, entkoppelter Services mit minimalem Overhead.
Prototyping: Schnelles Erstellen von Prototypen und Testservern.
Middleware-Plattformen: Authentifizierungs- und Logging-Dienste auf Basis von Middleware-Ketten.
3.3.6. Fazit¶
Hono ist eine moderne und leichtgewichtige Alternative zu Frameworks wie Express.js oder Fastify, mit Fokus auf Performance, Standardkonformität und Einsatz in Edge-Umgebungen. Dank der einfachen API und guten TypeScript-Unterstützung eignet sich Hono sowohl für Anfänger als auch für erfahrene Entwickler.
3.4. Allgemein REST API Architektur¶
REST steht für Representational State Transfer und ist ein Architekturstil zur Gestaltung von verteilten Systemen, insbesondere Webanwendungen. REST wurde ursprünglich von Roy Fielding im Rahmen seiner Dissertation definiert und bildet heute die Grundlage für viele moderne Web-APIs. Eine REST-API ermöglicht die strukturierte Kommunikation zwischen Client (z. B. einem React-Frontend) und Server über das HTTP-Protokoll.
Ziel von REST ist es, Ressourcen über klar definierte URLs bereitzustellen und deren Zustand mithilfe standardisierter HTTP-Methoden wie GET, POST, PUT oder DELETE zu verändern oder abzurufen. REST wird häufig für CRUD-Operationen verwendet – also Create (Erstellen), Read (Lesen), Update (Aktualisieren) und Delete (Löschen) von Daten.
Wichtige Merkmale von REST-APIs:
Zustandslosigkeit: Jeder Request enthält alle nötigen Informationen zur Verarbeitung. Der Server speichert keinen Client-Zustand zwischen den Anfragen.
Klare Ressourcendefinition: Jede Information (z. B. ein Benutzer, ein Artikel) wird als Ressource dargestellt und ist über eine eindeutige URL erreichbar.
Verwendung von HTTP-Standards: HTTP-Methoden, Statuscodes, Header und Caching-Verhalten sind vordefiniert und klar verständlich.
Trennung von Client und Server: Der Client kennt nur die API-Schnittstelle, nicht deren interne Implementierung – das erleichtert die Weiterentwicklung beider Seiten.
Eine typische REST-Request besteht aus mehreren Teilen:
HTTP-Methode: Legt fest, welche Aktion durchgeführt wird (z. B.
GETfür Abfragen,POSTzum Erstellen).Endpunkt (URL): Gibt an, welche Ressource angesprochen wird, z. B.
/api/users/42.Header: Enthält Metadaten wie
Content-TypeoderAuthorization.Body (optional): Transportiert Daten zum Server, z. B. bei POST- oder PUT-Anfragen im JSON-Format.
Beispiel – eine POST-Anfrage zum Anlegen eines Benutzers:
POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
Authorization: Bearer <token>
{
"name": "Max Mustermann",
"email": "max@example.com"
}
Die Serverantwort (Response) könnte wie folgt aussehen:
HTTP/1.1 201 Created
Content-Type: application/json
{
"id": 42,
"name": "Max Mustermann"
}
REST nutzt das HTTP-Protokoll als Transportweg. Der Client (z. B. eine React-App) sendet Anfragen an klar definierte Endpunkte des Servers. Der Server verarbeitet die Anfrage, ruft ggf. Daten aus einer Datenbank ab, und sendet die Antwort zurück. Da REST zustandslos ist, kann jeder Request unabhängig behandelt werden – das ist besonders vorteilhaft für die horizontale Skalierung (z. B. über Load Balancer).
REST-APIs sind aufgrund folgender Eigenschaften besonders gut für den Einsatz im Web geeignet:
Skalierbarkeit: Zustandslosigkeit erlaubt verteilte Architekturen und einfache Lastverteilung.
Lesbarkeit & Wartbarkeit: Durch die klare Trennung von Ressourcen und Methoden sind APIs leicht verständlich.
Kompatibilität: REST nutzt Standardtechnologien (HTTP, JSON), die auf jeder Plattform verfügbar sind.
Cachbarkeit: GET-Anfragen können effizient vom Client oder Proxy zwischengespeichert werden.
Erweiterbarkeit: Neue Endpunkte oder Datenfelder können leicht ergänzt werden, ohne bestehende Clients zu brechen.
200 OK: Erfolgreiche Anfrage mit Antwortdaten201 Created: Neue Ressource erfolgreich erstellt204 No Content: Erfolgreiche Anfrage ohne Rückgabedaten400 Bad Request: Fehlerhafte Anfrage (z. B. ungültige Daten)401 Unauthorized: Authentifizierung erforderlich404 Not Found: Ressource nicht gefunden500 Internal Server Error: Allgemeiner Serverfehler
Ein REST-Endpunkt beschreibt immer eine Ressource. Beispielsweise:
GET /products→ Liste aller ProdukteGET /products/123→ Produkt mit ID 123POST /products→ Neues Produkt erstellenPUT /products/123→ Produkt 123 aktualisierenDELETE /products/123→ Produkt 123 löschen
Diese Struktur ist intuitiv und wird von Entwickler*innen schnell verstanden – unabhängig von Programmiersprache oder Framework.
REST ist ein universeller und leicht verständlicher Ansatz zur API-Entwicklung. In modernen Frontend-Stacks wie React spielt REST eine zentrale Rolle, um Daten mit dem Backend auszutauschen. Durch die Nutzung standardisierter Protokolle, Methoden und Formate ermöglicht REST eine robuste, wartbare und skalierbare Schnittstellenarchitektur – ideal für Webanwendungen jeder Größe.
3.5. Datenkommunikation zwischen React‑Frontend und Backend¶
Eine performante, zuverlässige und gut wartbare Kommunikation zwischen Front‑ und Backend ist das Rückgrat jeder modernen Web‑Anwendung. In React gibt es verschiedene Strategien, HTTP‑Requests zu organisieren, Fehler zu behandeln und Daten zu cachen. Dieser Abschnitt stellt bewährte Vorgehensweisen vor und zeigt anhand von Code‑Beispielen, wie sie im Zusammenspiel mit Hono und dem React‑Ökosystem umgesetzt werden können.
Warum überhaupt eine eigene „Daten‑Schicht“?
3.6. Typsichere APIs mit tRPC¶
tRPC ist ein Typescript‑first‑Framework, das Ende‑zu‑Ende‑Typsicherheit bietet, ohne dass ein zusätzliches Schema‑Format (etwa OpenAPI oder GraphQL) gepflegt werden muss. Server‑ und Client‑Code teilen sich dieselben TypeScript‑Definitionen; dadurch entstehen zero‑cost Typ‑Garantien und weniger Fehlabgleiche zwischen Front‑ und Backend.
import { createTRPCReact } from '@trpc/react-query'
import type { AppRouter } from '../server'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
const trpc = createTRPCReact<AppRouter>()
const qc = new QueryClient()
function Hello() {
const { data } = trpc.hello.useQuery({ name: 'Max' })
return <p>{data}</p> {/* ⟶ "Hello Max" (typsicher) */}
}
export default function App() {
return (
<trpc.Provider client={trpc.createClient({ url: '/trpc' })} queryClient={qc}>
<QueryClientProvider client={qc}>
<Hello />
</QueryClientProvider>
</trpc.Provider>
)
}
3.7. Literatur¶
React (Offizielle Dokumentation) https://react.dev/
React – Hooks API Reference https://react.dev/reference/react
TypeScript (Offizielle Dokumentation) https://www.typescriptlang.org/docs/
Hono (Offizielle Dokumentation) https://hono.dev/docs
tRPC (Offizielle Dokumentation) https://trpc.io/docs
TanStack Router https://tanstack.com/router/latest/docs/framework/react
TanStack Query https://tanstack.com/query/latest/docs/framework/react
Docker – Get Started https://docs.docker.com/get-started/
PostgreSQL Dokumentation https://www.postgresql.org/docs/
pnpm (Schneller Paketmanager) https://pnpm.io/
Vite (Build Tool für moderne Webprojekte) https://vitejs.dev/guide/