API-Service für die Produktverwaltung
In diesem Kapitel erstellen wir einen API-Service, der für die Kommunikation mit dem Backend verantwortlich ist. Dieser Service wird alle notwendigen Funktionen zum Abrufen, Erstellen, Aktualisieren und Löschen von Produkten bereitstellen. Nach Abschluss dieses Kapitels haben Sie eine funktionierende API-Integration in Ihrer React-Anwendung.
Schritt 1: Umgebungsvariablen einrichten
Abschnitt betitelt „Schritt 1: Umgebungsvariablen einrichten“Erstellen Sie im Root-Verzeichnis Ihres Projekts eine Datei mit dem Namen .env:
VITE_API_BASE_URL=http://localhost:4000Diese Datei speichert die Basis-URL für unsere API-Anfragen. Durch die Verwendung von Umgebungsvariablen können wir die API-URL leicht ändern, wenn wir zu einer anderen Umgebung (z.B. Produktion) wechseln.
Schritt 2: Formatierungshilfsfunktion erstellen
Abschnitt betitelt „Schritt 2: Formatierungshilfsfunktion erstellen“Erstellen Sie im Verzeichnis src/utils eine Datei formatDate.js:
import dayjs from "dayjs";
function formatDate(timestamp) { // Create a Day.js object from the timestamp // Assuming the timestamp is in milliseconds return dayjs(timestamp).format('DD.MM.YYYY, HH:mm') + ' Uhr';}
export default formatDate;Diese Hilfsfunktion wird für die Formatierung von Zeitstempeln verwendet, die wir möglicherweise vom Server erhalten.
Schritt 3: API-Service für Produkte erstellen
Abschnitt betitelt „Schritt 3: API-Service für Produkte erstellen“Erstellen Sie im Verzeichnis src/services eine Datei productApi.js:
const API_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:4000';
// Helper function to remove MongoDB-specific fields from dataconst cleanMongooseData = (data) => { if (!data) return data; const { _id, __v, createdBy, createdAt, updatedAt, ...cleanData } = data; return cleanData;};
// Default fetch options with credentials includedconst fetchWithAuth = async (url, options = {}) => { const defaultOptions = { credentials: 'include', // Include cookies in all requests headers: { 'Content-Type': 'application/json', ...options.headers } };
const mergedOptions = { ...defaultOptions, ...options, headers: { ...defaultOptions.headers, ...options.headers } };
const response = await fetch(url, mergedOptions);
if (!response.ok) { // Handle 401 Unauthorized errors specially if (response.status === 401) { console.error('Authentication required. Please log in again.'); }
// Try to get error message from response let errorMessage; try { const errorData = await response.json(); errorMessage = errorData.message || `HTTP Error: ${response.status}`; } catch (e) { errorMessage = `HTTP Error: ${response.status}`; }
throw new Error(errorMessage); }
return response.json();};
const api = { // Get products getProducts: async () => { try { return await fetchWithAuth(`${API_URL}/product`); } catch (error) { console.error('Error fetching products:', error); throw error; } },
// Create product createProduct: async (productData) => { try { // Remove MongoDB-specific fields const cleanedData = cleanMongooseData(productData);
return await fetchWithAuth(`${API_URL}/product`, { method: 'POST', body: JSON.stringify(cleanedData) }); } catch (error) { console.error('Error creating product:', error); throw error; } },
// Update product updateProduct: async (id, productData) => { try { // Remove MongoDB-specific fields const cleanedData = cleanMongooseData(productData);
return await fetchWithAuth(`${API_URL}/product/${id}`, { method: 'PUT', body: JSON.stringify(cleanedData) }); } catch (error) { console.error(`Error updating product with id ${id}:`, error); throw error; } },
// Delete product deleteProduct: async (id) => { try { return await fetchWithAuth(`${API_URL}/product/${id}`, { method: 'DELETE' }); } catch (error) { console.error(`Error deleting product with id ${id}:`, error); throw error; } }};
export default api;Schritt 4: Produktseite aktualisieren
Abschnitt betitelt „Schritt 4: Produktseite aktualisieren“Erweitern wir unsere Produktseite, um Produkte vom API zu laden und anzuzeigen. Aktualisieren Sie die Datei src/pages/Products.jsx:
import { useState, useEffect } from 'react';import { Container, Alert, Spinner, Table } from 'react-bootstrap';import api from '../services/productApi.js';
const Products = () => { const [products, setProducts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null);
useEffect(() => { fetchProducts(); }, []);
const fetchProducts = async () => { try { setLoading(true); const data = await api.getProducts(); setProducts(data); setError(null); } catch (err) { setError('Fehler beim Laden der Produkte. Bitte versuchen Sie es später erneut.'); console.error(err); } finally { setLoading(false); } };
return ( <Container className="py-4"> <h1 className="mb-4">Produkte</h1>
{error && <Alert variant="danger">{error}</Alert>}
<h2 className="mb-3">Produktliste</h2>
{loading ? ( <div className="text-center my-5"> <Spinner animation="border" role="status"> <span className="visually-hidden">Lade Produkte...</span> </Spinner> </div> ) : products.length > 0 ? ( <Table striped bordered hover responsive> <thead> <tr> <th>Titel</th> <th>Preis</th> <th>Status</th> </tr> </thead> <tbody> {products.map(product => ( <tr key={product._id}> <td> <strong>{product.title}</strong> <br/> {product.description} </td> <td>{parseFloat(product.price).toFixed(2)} CHF</td> <td> {product.active ? <span className="text-success">Aktiv</span> : <span className="text-secondary">Inaktiv</span> } </td> </tr> ))} </tbody> </Table> ) : ( <Alert variant="info">Keine Produkte gefunden.</Alert> )} </Container> );};
export default Products;Schritt 5: Anwendung testen
Abschnitt betitelt „Schritt 5: Anwendung testen“Starten Sie die Anwendung mit:
npm run devUm die API-Integration zu testen, benötigen Sie einen laufenden Backend-Server. Für Testzwecke können Sie entweder:
- Den Back-End-Server starten, falls Sie bereits einen haben, oder
- Verwenden Sie die Mock-Daten in der Produktseite, indem Sie die fetch-Funktion vorübergehend auskommentieren und stattdessen ein Array mit Beispielprodukten verwenden.
Beispiel für Mock-Daten (fügen Sie dies in der Products-Komponente hinzu, falls Sie keinen Backend-Server haben):
// Mock-Daten zum Testen ohne Backendconst mockProducts = [ { _id: '1', title: 'Laptop XPS 15', description: 'Leistungsstarker Laptop für Profis', price: 1599.99, active: true }, { _id: '2', title: 'Smartphone Galaxy S22', description: 'Neuestes Smartphone mit verbesserter Kamera', price: 899.99, active: true }, { _id: '3', title: 'Kopfhörer Noise Cancelling', description: 'Kabellose Kopfhörer mit aktiver Geräuschunterdrückung', price: 249.99, active: false }];
// Ersetzen Sie den Aufruf zu api.getProducts() durch:// setProducts(mockProducts);Was passiert hier?
Abschnitt betitelt „Was passiert hier?“- API-Service: Wir haben einen zentralen Service erstellt, der alle API-Aufrufe verwaltet
- Umgebungsvariablen: Wir nutzen Vite’s Umgebungsvariablen, um die API-URL zu konfigurieren
- Error Handling: Wir fangen Fehler ab und zeigen benutzerfreundliche Fehlermeldungen an
- Daten-Darstellung: Wir verwenden React-Bootstrap-Komponenten, um die Produktdaten ansprechend anzuzeigen
Zusammenfassung
Abschnitt betitelt „Zusammenfassung“In diesem Abschnitt haben wir:
- Einen API-Service erstellt, der für die Kommunikation mit dem Backend zuständig ist
- Eine Hilfsfunktion für die Datumsformatierung implementiert
- Die Produktseite aktualisiert, um Daten vom Server zu laden und anzuzeigen
- Benutzerfreundliche Lade- und Fehlerzustände eingebaut
Unsere Anwendung kann jetzt Produktdaten vom Server abrufen und anzeigen. Im nächsten Kapitel werden wir das Produktformular implementieren, um neue Produkte zu erstellen und bestehende zu bearbeiten.