Skip to content

Instantly share code, notes, and snippets.

@lowsky

lowsky/blog2.md Secret

Last active April 17, 2018 22:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lowsky/71a8382032b39c98cfc354fc48d251d2 to your computer and use it in GitHub Desktop.
Save lowsky/71a8382032b39c98cfc354fc48d251d2 to your computer and use it in GitHub Desktop.
gql client

GraphQL im Mobile Client mit React Native und Expo: Introduction

Sobald wir mit einen GraphQL-Client unserem GraphQL-Server aus dem 1. Teil Anfragen schicken, werden die Vorteile von GraphQL noch leichter 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.

teaser: Spotify-Daten in einer React-Native-App via GraphQL client laden

Für eine schnelle Umsetzung - in wenigen Minuten - können wir Expo, also React Native, benutzen - 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

  1. Apollo Boost erlaubt durch Rückgriff auf Standardwerte mit wenigster Konfiguration starten zu können.
  2. Seit Apollo React 2.1 können sog. renderProps genutzt werden.
  3. Mit Apollo Client 2 wurden HOC (Higher Order Components) als Standard eingeführt, die reine UI Komponenten und 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 die 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
  • Einfache 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

prepare query of artists in graphiql

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: <ArtistsWithData name={/*...*/} />

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.

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 durch 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment