GraphQL im Client: Introduction
Sobald wir mit einen GraphQL-Client, hier mit Apollo, Anfragen an unserem GraphQL-Server aus dem 1. Teil schicken, werden die Vorteile von GraphQL noch besser sichtbar.
Wir werden sehen, wie
- leicht GraphQL im Client hinzugefügt werden kann,
- die React-UI-Komponenten mit nur geringem Aufwand erweitert werden können,
- GraphQL zum deklarativem Ansatz von React passt,
- GraphiQL die schnelle Anknüpfung an Schema und Server unterstützt,
- durch den Einsatz einer Bibliothek Aspekte wie Fetching, Error Handling und Caching von der Business-Logik isoliert bleiben und
- sogenannte Co-Location hilft, Wartbarkeit zu erhöhen.
In unserem Beispiel werden wir Spotify-Daten auf einem mobilen Gerät in einer React-Native-App vom eigenen GraphQL-Server laden.
Für eine schnelle Umsetzung – in wenigen Minuten – können wir Expo, also React Native, benutzen (mehr dazu im Block weiter unten) – Eine Demoversion wird einfach auf expo.io veröffentlicht.
Bibliothek/Integration
Wir werden die Apollo-2-Client-Bibliothek verwenden, die leichter einzusetzen ist als Facebooks eigene Bibliothek Relay. Relay kann zwar noch leistungsfähiger mit schlechten Netzverbindungen umgehen, würde aber mehr Komplexität und eine höhere Lernkurve mitbringen, und auch das Schema müsste daraufhin erweitert und angepasst werden.
GraphQL im Client mit Apollo und React
Apollo-Grundlagen
Mit Apollo werden die Daten, ähnlich wie bei flux/redux
, in einem lokalen State oder Cache gehalten. Die UI-Komponenten werden dann per HOC(Higher Order Component)-Ansatz gekapselt, an diesen State gekoppelt und mit den vom Server geladenen Werten gefüllt. Dadurch kann die Verbindung zum Server an zentraler Stelle zu konfiguriert werden. Apollo Version 1 baute noch direkt auf redux
auf, aber mit Version 2 wurde es unabhängig von einem redux-store
, modelliert den Cache selbst und bringt eine verbesserte Modularisierung mit sich.
Diese Komponenten stehen zur Verfügung:
- ApolloClient: Verbindung von Cache und Netzwerk Stack/Link
- ApolloInMemoryCache: clientseitiger GraphQL-Cache mit Normalisierung (ist Default-Implementierung)
- ApolloLink: Netzwerk-Verbindung und Schnittstelle zum GraphQL-Server
- ApolloProvider: Verbindung zwischen ApolloClient und den Komponenten in der Komponenten-Hierarchie
Installation der Bibliotheken und Benutzung in React
- Apollo Boost erlaubt durch Rückgriff auf Standardwerte mit wenigster Konfiguration starten zu können.
- Seit Apollo React 2.1 können sog.
renderProps
genutzt werden. - Mit Apollo Client 2 wurden HOC (Higher Order Components) als Standard eingeführt, die reine UI-Komponenten von dem GraphQL-Glue-Code trennt. Der letzte Ansatz erlaubt leichtere Wiederverwendung und vor allem Testen und soll deshalb hier benutzt werden.
Nach Installation folgender Bibliotheken mit
npm install apollo-client-preset react-apollo graphql-tag graphql --save
können wir auf oberster Ebene den Kontext mit der Verbindung zum GraphQL-Server definieren.
// app.js
import { ApolloClient, InMemoryCache } from 'apollo-client-preset';
import { HttpLink } from 'apollo-link-http';
const client = new ApolloClient({
link: new HttpLink({
uri: 'http://localhost:3000' // GraphQL endpoint
}),
cache: new InMemoryCache()
});
import { ApolloProvider } from 'react-apollo';
import Hauptkomponente from './Hauptkomponente';
// rendering in a react web app:
ReactDOM.render(
<ApolloProvider client={client}>
<Hauptkomponente />
</ApolloProvider>,
document.getElementById('root')
);
Die Komponenten werden nun einfach per Apollo gekapselt und dadurch um die GraphQL-Abfrage erweitert.
// Hauptkomponente.js
import gql from 'graphql-tag';
class ReactKomponente extends React.Component {
//...
}
// Anreicherung um GraphQL Abfrage
export default graphql(
gql`{ ... }`, // ES6 template string mit GraphQL query
config, // optional
)(ReactKomponente);
Dabei werden die Daten per React-Mechanismen als „props“ in die Komponente übergeben, dazu gleich mehr.
Abfrage-Parametrisierung mit Variablen
Um Variablen in GraphQL-Abfragen zu verwenden, kann das optionale `config-Object benutzt werden:
let config = {
options: (props) => ({
variables: {
byName: props.name
}
})
};
In diesem Beispiel wird die Variable byName
aus den props der React-Komponente mit dem Wert des name
-Feldes gefüllt, also zum Beispiel mit Xul Zolar
, wenn die gekapselte Komponente folgendermaßen verwendet wird:
<GekapselteReactKomponente name='Xul Zolar' />
Durch diesen deklarativen Ansatz lässt sich GraphQL mit React-Mechanismen ohne viel Aufwand in das UI integrieren.
Mobiler Client: Spotify Music Info App
Schritte für die Umsetzung in unserem Spotify-Beispiel:
- Tool-Installation: Expo, Android/iOS-Simulator oder Expo-App auf Smartphone (siehe Absatz “Expo…“)
- Projekt initialisieren mit Expo
- Einfaches UI und einfaches Styling
- Modellierung der Datenabfrage mit GraphiQL
- Verbindung zum Server und Verknüpfung der Komponente mit GraphQL-Abfrage
Projekt initialisieren mit Expo
Wir installieren das Projekt mit Expo:
exp init spotify-graphql-client-react-native-expo --projectType blank
Einfaches UI und Styling
Die generierte Seite muss noch abgeändert werden. Der Rückgabewert der render
-Method
// app.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Initial UI, more to come!</Text>
</View>
);
}
};
wird mit
<View style={styles.container}>
<Query />
<Artists name={this.state.artistName} />
</View>
ersetzt und die folgenden Zeilen hinzugefügt:
const Query = ({search}) => <SearchBar
onChangeText={search}
placeholder="artist name..." />;
const Artists = ({artists}) => (
<View>
{
artists.map(artist => (
<Text>
{ artist.name }
</Text>
))
}
</View>);
Die Liste ist anfangs noch leer.
Verbindung zum Server
Wir nennen die Datei app.js
in main.js
um, damit wir sie in folgender, neuer app.js
neu inkludieren können, und um sie in folgender Apollo-HOC-Komponente zu kapseln:
//app.js
import { ApolloClient, InMemoryCache } from 'apollo-client-preset';
import { HttpLink } from 'apollo-link-http';
import React from 'react';
import { ApolloProvider } from 'react-apollo';
import Main from './main';
const client = new ApolloClient({
link: new HttpLink({
uri: 'http://spotify-graphql-server.herokuapp.com/graphql'
}),
cache: new InMemoryCache()
});
export default class App extends React.Component {
render() {
return (
<ApolloProvider client={client}>
<Main />
</ApolloProvider>
);
}
}
Modellierung der Datenabfrage mit GraphiQL
online GraphIQL-Abfrage
Verknüpfung der Komponente mit GraphQL-Abfrage
In unserem Beispiel fragen wir nach der Liste der Künstler mit einem bestimmten Namen.
Dazu ergänzen wir in main.js
diese Suche, in einem gql
template string gekapselt (aus dem graphql-tag module
), mit Suchparameter:
const ArtistsQuery = gql`
query artists($byName: String!) {
artists: queryArtists(byName: $byName) {
name
}
}`;
const queryConfig = {
options: ({ byName }) => ({
variables: {
byName
}
})
};
const ArtistsWithData = graphql(ArtistsQuery, queryConfig)(Artists);
und verwenden weiter oben ArtistsWithData
anstatt Artists
, also:
Expo als Entwicklungsumgebung
Ein kurzer Überblick über das Setup und die Tools für die schnelle Entwicklung mit Android/iOS-Simulator oder Client auf Smartphone für unser Beispiel:
Tools und Projekt initialisieren
npm install exp --global
exp register
exp login
exp init spotify-graphql-client-react-native-expo --projectType blank
cd spotify-graphql-client-react-native-expo
exp start
[exp] Your URL is: exp://xxxx.spot-cli.exp.direct:80
[exp] Logs for your project will appear below. Press Ctrl+C to exit.
in simulator, open expo, + button, add url and ...
Auf dem Smartphone kann der erzeugte QR-Code oder der URL in der Expo-App eingescannt werden, so dass auch auf einem echten Gerät getestet werden kann.
Diese kann auch im Simulator genutzt werden. Am einfachsten funktioniert es mit dem Expo Cli Tool per
# in neuer shell, iOS Simulator oder Android Simulator
exp ios
exp android
Einige weiterführende Anwendungsfelder konnten nicht betrachtet werden, sind aber wesentlich und nutzen weitere Möglichkeiten von GraphQL:
- Verschachtelung der Komponenten und deren Abfragen, also zusätzlich Liste der Alben pro Künstler
- Änderungen per GraphQL-
mutation
: z. B. einen Künstler „liken“ - Optimistic UI: sofortige Aktualisierung der Anzeige, ohne auf Rückgabe des Servers zu warten
- Caching: Der Cache im Client kann sehr flexibel benutzt werden und z. B. schon bei Start mit alten Werten aus dem Local-Cache gefüllt werden etc.
- GraphQL-Subscription: Das interessanteste Feature ist die Möglichkeit, vom Server per Websockets (oder auch MQTT etc.) aktualisiert zu werden, wobei das gleiche Typsystem des Schemas benutzt wird.
Zusammenfassung
In diesem Artikel haben wir anhand eines Beispiels sehen können, wie einfach die Integration von GraphQL im React Client ist.
Gegenüber reinem REST-Ansatz kann exakt spezifiziert werden,
welche Daten angefordert werden sollen.
Apollo bringt als client framework bereits Grundfunktionen mit, die
sonst immer zusätzlich implementiert werden müssten, wie zum Beispiel Caching, Aktualisierung des Clients…
Vorteile:
- Exakte Definition, welche Daten benötigt und übermittelt werden sollen
(kein „over-fetching“ mehr): ideal für mobile Datenverbindung - Abfrage durch Aggregation auf Server-Seite mit nur einem Request (kein typisches n+1 Muster): optimal bei mobiler Datenverbindung
- API-Entwicklung aus Business Sicht, oder den Anforderungen des Clients ist zielorienter und bleibt eventuell „leichter verständlich“
- ein „Vertrag“ durch Typisierung im Schema, der immer erlaubt, jede Abfrage
gegen die aktuelle Schema-Definition zu überprüfen: keine „unnötigen“ Fehler durch fehlerhafte Abfragen,
weil zum beispiel die JSON-Antwort unterschiedliche Feldnamen enthält (wie „Name“ statt „name“) - Nur ein einziger Endpoint für Aggregation von verschiedenen Datenquellen notwendig.
Durch die Typisierung erhält man bereits einen sehr guten Tool-Support in einer IDE, die durch Syntaxprüfung und Code Completion Arbeit sparen. Auch ein Linter, wie z. B. eslint kann die GraphQL-Abfragen im Sourcecode parsen und Fehler finden, noch bevor manuell getestet werden muss – was viel Zeit spart und mühevolle Fehlersuche vermeidet.
Durch den deklarativen Ansatz und die Möglichkeit, GraphQL und Komponenten im Sourcecode zusammen zu speichern, fallen Anpassungen und Orientierung im Code leicht. Erweiterungen um einfache Felder sind z. B. völlig unkompliziert.
Aus meiner Sicht ist auch der Austausch über die Schnittstelle zwischen Front- und Backend- Teams durch den technischen Kontrakt erleichtert. Der Dokumentations- und Kommunikations-Overhead wird reduziert.
Das Repository findet sich hier auf Github
Viel Spaß beim Ausprobieren!
Weitere Beiträge
von Robert Hostlowsky
Dein Job bei codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
Weitere Artikel in diesem Themenbereich
Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog-Autor*in
Robert Hostlowsky
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.