In der Webentwicklung war die Optimierung und Skalierung von Anwendungen schon immer ein Thema. React.js hatte außergewöhnlichen Erfolg in der Frontend-Entwicklung als Werkzeug, das eine robuste Möglichkeit bietet, Benutzeroberflächen zu erstellen. Aber es wird kompliziert mit wachsenden Anwendungen, insbesondere wenn es um mehrere REST-API-Endpunkte geht. Bedenken wie Over-Fetching, bei dem übermäßige Daten benötigt werden, können eine Quelle für Leistungsengpässe und eine schlechte Benutzererfahrung sein.
Eine der Lösungen für diese Herausforderungen besteht darin, die Verwendung von GraphQL in React-Anwendungen zu übernehmen. Wenn Ihr Backend mehrere REST-Endpunkte hat, kann die Einführung einer GraphQL-Schicht, die intern Ihre REST-API-Endpunkte aufruft, Ihre Anwendung vor Overfetching schützen und Ihre Frontend-Anwendung optimieren. In diesem Artikel erfahren Sie, wie Sie es verwenden, die Vor- und Nachteile dieses Ansatzes, verschiedene Herausforderungen und wie Sie diese angehen können. Wir werden auch tiefer in einige praktische Beispiele eintauchen, wie GraphQL Ihnen helfen kann, die Art und Weise, wie Sie mit Ihren Daten arbeiten, zu verbessern.
Overfetching in REST-APIs
In REST-APIs tritt Overfetching auf, wenn die Menge an Daten, die die API an den Client liefert, größer ist als das, was der Client benötigt. Dies ist ein häufiges Problem bei REST-APIs, die oft ein festes Objekt oder Antwortschema zurückgeben. Um dieses Problem besser zu verstehen, betrachten wir ein Beispiel.
Betrachten Sie eine Benutzerprofilseite, auf der nur der Name
und die E-Mail
des Benutzers angezeigt werden sollen. Bei einer typischen REST-API könnte das Abrufen der Benutzerdaten so aussehen:
fetch('/api/users/1')
.then(response => response.json())
.then(user => {
// Use the user's name and profilePicture in the UI
});
Die API-Antwort wird unnötige Daten enthalten:
{
"id": 1,
"name": "John Doe",
"profilePicture": "/images/john.jpg",
"email": "[email protected]",
"address": "123 Denver St",
"phone": "111-555-1234",
"preferences": {
"newsletter": true,
"notifications": true
},
// ...more details
}
Obwohl die Anwendung nur die Felder Name und E-Mail des Benutzers benötigt, gibt die API das gesamte Benutzerobjekt zurück. Diese zusätzlichen Daten erhöhen oft die Payload-Größe, benötigen mehr Bandbreite und können schließlich die Anwendung verlangsamen, wenn sie auf einem Gerät mit begrenzten Ressourcen oder einer langsamen Netzwerkverbindung verwendet wird.
GraphQL als Lösung
GraphQL löst das Overfetching-Problem, indem es den Clients ermöglicht, genau die Daten anzufordern, die sie benötigen. Durch die Integration eines GraphQL-Servers in Ihre Anwendung können Sie eine flexible und effiziente Datenabrufschicht erstellen, die mit Ihren bestehenden REST-APIs kommuniziert.
Wie es funktioniert
1. GraphQL-Servereinrichtung
Sie richten einen GraphQL-Server ein, der als Vermittler zwischen Ihrem React-Frontend und den REST-APIs fungiert.
2. Schema-Definition
Sie definieren ein GraphQL-Schema, das die Datentypen und Abfragen spezifiziert, die Ihr Frontend benötigt.
3. Implementierung der Resolver
Sie implementieren Resolver im GraphQL-Server, die Daten von den REST-APIs abrufen und nur die notwendigen Felder zurückgeben.
4. Integration im Frontend
Sie aktualisieren Ihre React-Anwendung, um GraphQL-Abfragen anstelle direkter REST-API-Aufrufe zu verwenden.
Dieser Ansatz ermöglicht es Ihnen, den Datenabruf zu optimieren, ohne Ihre bestehende Backend-Infrastruktur grundlegend zu ändern.
Implementierung von GraphQL in einer React-Anwendung
Lassen Sie uns sehen, wie man einen GraphQL-Server einrichtet und in eine React-Anwendung integriert.
Abhängigkeiten installieren
npm install apollo-server graphql axios
Das Schema definieren
Erstellen Sie eine Datei namens schema.js
:
const { gql } = require('apollo-server');
const typeDefs = gql`
type User {
id: ID!
name: String
email: String // Ensure this matches exactly with the frontend query
}
type Query {
user(id: ID!): User
}
`;
module.exports = typeDefs;
Dieses Schema definiert einen Typ User
und eine Abfrage user
, die einen Benutzer anhand der ID abruft.
Resolver implementieren
Erstellen Sie eine Datei namens resolvers.js
:
const resolvers = {
Query: {
user: async (_, { id }) => {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
const user = await response.json();
return {
id: user.id,
name: user.name,
email: user.email, // Return email instead of profilePicture
};
} catch (error) {
throw new Error(`Failed to fetch user: ${error.message}`);
}
},
},
};
module.exports = resolvers;
Der Resolver für die Abfrage user
ruft Daten aus der REST-API ab und gibt nur die erforderlichen Felder zurück.
Wir werden https://jsonplaceholder.typicode.com/ für unsere Fake-REST-API verwenden.
Server einrichten
Erstellen Sie eine Datei server.js
:
const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const resolvers = require('./resolvers');
const server = new ApolloServer({
typeDefs,
resolvers,
});
server.listen({ port: 4000 }).then(({ url }) => {
console.log(`GraphQL Server ready at ${url}`);
});
Starten Sie den Server:
node server.js
Ihr GraphQL-Server ist unter http://localhost:4000/graphql live, und wenn Sie Ihren Server abfragen, gelangen Sie zu dieser Seite.
Integration in die React-Anwendung
Wir werden nun die React-Anwendung ändern, um die GraphQL-API zu verwenden.
Apollo Client installieren
npm install @apollo/client graphql
Apollo Client konfigurieren
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:4000',
cache: new InMemoryCache(),
});
Die GraphQL-Abfrage schreiben
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
Integrieren Sie nun die obigen Codefragmente in Ihre React-App. Hier ist unten eine einfache React-App, die es einem Benutzer ermöglicht, die Benutzer-ID auszuwählen und die Informationen anzuzeigen:
import { useState } from 'react';
import { ApolloClient, InMemoryCache, ApolloProvider, gql, useQuery } from '@apollo/client';
import './App.css'; // Link to the updated CSS
const client = new ApolloClient({
uri: 'http://localhost:4000', // Ensure this is the correct URL for your GraphQL server
cache: new InMemoryCache(),
});
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
const User = ({ userId }) => {
const { loading, error, data } = useQuery(GET_USER, {
variables: { id: userId },
});
if (loading) return <p>Loading</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div className="user-container">
<h2>{data.user.name}</h2>
<p>Email: {data.user.email}</p>
</div>
);
};
const App = () => {
const [selectedUserId, setSelectedUserId] = useState("1");
return (
<ApolloProvider client={client}>
<div className="app-container">
<h1 className="title">GraphQL User Lookup</h1>
<div className="dropdown-container">
<label htmlFor="userSelect">Select User ID:</label>
<select
id="userSelect"
value={selectedUserId}
onChange={(e) => setSelectedUserId(e.target.value)}
>
{Array.from({ length: 10 }, (_, index) => (
<option key={index + 1} value={index + 1}>
{index + 1}
</option>
))}
</select>
</div>
<User userId={selectedUserId} />
</div>
</ApolloProvider>
);
};
export default App;
Ergebnis
Sie werden einfache Benutzerdetails wie diese sehen: [Github-Link].
Arbeiten mit mehreren Endpunkten
Stellen Sie sich ein Szenario vor, in dem Sie die Beiträge eines bestimmten Benutzers abrufen müssen, zusammen mit den einzelnen Kommentaren zu jedem Beitrag. Anstatt drei separate API-Aufrufe von Ihrer Frontend-React-App aus zu tätigen und mit unnötigen Daten umzugehen, können Sie den Prozess mit GraphQL optimieren. Durch Definition eines Schemas und Erstellung einer GraphQL-Abfrage können Sie nur die genauen Daten anfordern, die Ihre Benutzeroberfläche benötigt, was einen effizienten einmaligen Aufruf darstellt.
Wir müssen Benutzerdaten, ihre Beiträge und Kommentare für jeden Beitrag von den verschiedenen Endpunkten abrufen. Wir werden fetch verwenden, um Daten von den verschiedenen Endpunkten zu sammeln und sie über GraphQL zurückzugeben.
Aktualisieren der Resolver
const fetch = require('node-fetch');
const resolvers = {
Query: {
user: async (_, { id }) => {
try {
// fetch user
const userResponse = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
const user = await userResponse.json();
// fetch posts for a user
const postsResponse = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${id}`);
const posts = await postsResponse.json();
// fetch comments for a post
const postsWithComments = await Promise.all(
posts.map(async (post) => {
const commentsResponse = await fetch(`https://jsonplaceholder.typicode.com/comments?postId=${post.id}`);
const comments = await commentsResponse.json();
return { post, comments };
})
);
return {
id: user.id,
name: user.name,
email: user.email,
posts: postsWithComments,
};
} catch (error) {
throw new Error(`Failed to fetch user data: ${error.message}`);
}
},
},
};
module.exports = resolvers;
Aktualisieren des GraphQL-Schemas
const { gql } = require('apollo-server');
const typeDefs = gql`
type Comment {
id: ID!
name: String
email: String
body: String
}
type Post {
id: ID!
title: String
body: String
comments: [Comment]
}
type User {
id: ID!
name: String
email: String
posts: [Post]
}
type Query {
user(id: ID!): User
}
`;
module.exports = typeDefs;
Der Serveraufbau in server.js
bleibt gleich. Sobald wir den React.js-Code aktualisieren, erhalten wir die folgende Ausgabe:
Ergebnis
Sie werden einen detaillierten Benutzer wie diesen sehen: [Github-Link].
Vorteile dieses Ansatzes
Das Integrieren von GraphQL in Ihre React-Anwendung bietet mehrere Vorteile:
Vermeidung von Overfetching
Ein wichtiges Merkmal von GraphQL ist, dass es nur genau das abruft, was angefordert wird. Der Server gibt nur die angeforderten Felder zurück und stellt sicher, dass die Menge der über das Netzwerk übertragenen Daten reduziert wird, indem nur das bereitgestellt wird, was die Abfrage verlangt, wodurch die Leistung verbessert wird.
Vereinfachung des Front-End-Codes
GraphQL ermöglicht es Ihnen, die benötigten Informationen in einer einzigen Abfrage zu erhalten, unabhängig von deren Ursprung. Intern könnte es 3 API-Aufrufe machen, um die Informationen zu erhalten. Dies hilft, Ihren Frontend-Code zu vereinfachen, da Sie nun nicht mehr verschiedene asynchrone Anforderungen orchestrieren und deren Ergebnisse kombinieren müssen.
Verbesserung der Entwicklererfahrung
Eine starke Typisierung und Schema-Introspektion bieten bessere Werkzeuge und Fehlerüberprüfung als in der traditionellen API-Implementierung. Darüber hinaus gibt es interaktive Umgebungen, in denen Entwickler Abfragen erstellen und testen können, einschließlich GraphiQL oder Apollo Explorer.
Bewältigung von Komplexitäten und Herausforderungen
Dieser Ansatz hat einige Vorteile, bringt aber auch Herausforderungen mit sich, die gemanagt werden müssen.
Zusätzliche Backend-Schicht
Die Einführung des GraphQL-Servers schafft eine zusätzliche Schicht in Ihrer Backend-Architektur, und wenn sie nicht richtig verwaltet wird, wird sie zu einem einzelnen Ausfallpunkt.
Lösung
Achten Sie auf Fehlerbehandlung und Überwachung. Containerisierung und Orchestrierungstools wie Docker und Kubernetes können helfen, Skalierbarkeit und Zuverlässigkeit zu verwalten.
Potenzielle Leistungsüberlastung
Der GraphQL-Server kann mehrere REST-API-Aufrufe durchführen, um eine einzige Abfrage aufzulösen, was zu Latenz und Überlastung im System führen kann.
Lösung
Speichern Sie die Ergebnisse im Cache, um mehrere Aufrufe an die API zu vermeiden. Einige Tools, wie DataLoader, können den Prozess des Batchens und Cachens von Anforderungen verwalten.
Fazit
„Einfachheit ist die ultimative Raffinesse“ — Leonardo da Vinci
Die Integration von GraphQL in Ihre React-Anwendung ist mehr als nur eine Leistungsoptimierung — es ist ein strategischer Schritt hin zur Entwicklung von wartbareren, skalierbareren und effizienteren Anwendungen. Durch die Bewältigung von Overfetching und die Vereinfachung des Datenmanagements verbessern Sie nicht nur die Benutzererfahrung, sondern statten auch Ihr Entwicklungsteam mit besseren Tools und Praktiken aus.
Obwohl die Einführung einer GraphQL-Schicht ihre eigenen Herausforderungen mit sich bringt, überwiegen die Vorteile oft die Komplexitäten. Durch sorgfältige Planung Ihrer Implementierung, Optimierung Ihrer Resolver und Sicherung Ihrer Endpunkte können potenzielle Nachteile minimiert werden. Darüber hinaus kann die Flexibilität, die GraphQL bietet, Ihre Anwendung zukunftssicher machen, während sie wächst und sich weiterentwickelt.
Die Akzeptanz von GraphQL bedeutet nicht, dass Sie Ihre bestehenden REST-APIs aufgeben müssen. Stattdessen ermöglicht es Ihnen, ihre Stärken zu nutzen, während Sie eine effizientere und flexiblere Datenzugriffsschicht für Ihre Front-End-Anwendungen bereitstellen. Dieser hybride Ansatz kombiniert die Zuverlässigkeit von REST mit der Agilität von GraphQL und bietet Ihnen das Beste aus beiden Welten.
Wenn Sie bereit sind, Ihre React-Anwendung auf die nächste Stufe zu heben, sollten Sie in Betracht ziehen, GraphQL in Ihre Datenabrufstrategie zu integrieren. Die Reise mag Herausforderungen mit sich bringen, aber die Belohnungen — ein reibungsloserer Entwicklungsprozess, glücklichere Entwickler und zufriedene Benutzer — machen sie zu einem lohnenswerten Unterfangen.
Vollständiger Code verfügbar
Den vollständigen Code für diese Implementierung finden Sie in meinem GitHub-Repository.
Source:
https://dzone.com/articles/enhancing-react-applications-with-graphql-over-rest-apis