Apollo: Get started!

GraphQL Server auf Basis von React entwickeln
Keine Kommentare

Das öffentliche GitHub API ist das populärste Beispiel für eine GraphQL-Schnittstelle. Auch wenn GraphQL noch sehr weit davon entfernt ist, REST als führenden Standard für Webschnittstellen abzulösen, gewinnt es stetig an Beliebtheit.

GraphQL ist im Kern lediglich eine Abfragesprache, mit der Sie auf einem Client eine Anfrage nach bestimmten Informationen formulieren und dabei gleichzeitig das Format der Antwort des Servers vorgeben können. Der große Vorteil gegenüber REST ist, dass Sie mehrere Ressourcen gleichzeitig anfragen können und nur die Informationen erhalten, die Sie wirklich benötigen. Die Datenstrukturen werden in GraphQL in einem eigenen Typsystem beschrieben. Mit entsprechenden Werkzeugen können Sie daraus Typdefinitionen in TypeScript oder Flow erzeugen, die Sie auch clientseitig nutzen können. In einer Client-Server-Applikation kommen in der Regel auf beiden Seiten Bibliotheken zum Einsatz, die die meiste Arbeit übernehmen.

Eine der populärsten Clientbibliotheken ist der Apollo Client. Diese Bibliothek ist zwar unabhängig von einem bestimmten JavaScript-Framework, wird jedoch sehr häufig in Kombination mit React eingesetzt. In diesem Artikel erhalten Sie einen Einblick in die Welt von GraphQL und sehen, wie Sie einen GraphQL-Server auf Basis von Node.js entwickeln und ihn mit einer React-Applikation mithilfe von Apollo Client verbinden können.

Eine kurze Einführung in GraphQL

Die GraphQL-Spezifikation wurde ursprünglich von Facebook entwickelt. Initial wurde sie unter der viel kritisierten BSD-Lizenz mit Patenterweiterung lizenziert. Wie die meisten anderen Open-Source-Werkzeuge, die von Facebook entwickelt werden, wurde sowohl die Spezifikation als auch die JavaScript-Referenzimplementierung unter eine andere Lizenz gestellt. Die Spezifikation ist mittlerweile OWFa-Lizenz, die Referenzimplementierung unter der MIT-Lizenz verfügbar. GraphQL kann damit bedenkenlos im Unternehmenskontext eingesetzt werden. Da GraphQL im Kern lediglich eine Spezifikation ist, ist die Abfragesprache unabhängig von einer Programmiersprache und kann in JavaScript, Java oder auch Python eingesetzt werden. Für die meisten gängigen Programmiersprachen existieren mittlerweile sowohl Sever- als auch Clientimplementierungen.

Bei der Implementierung der Serverseite sollten Sie beachten, dass es sich bei GraphQL nicht um ein Framework, sondern lediglich eine Schicht in Ihrer Applikation handelt. Aus diesem Grund ist es auch möglich, GraphQL mit überschaubarem Aufwand in bestehende Applikationen zu integrieren. Ihre Businesslogik bleibt bestehen, nur die Kommunikationsschicht zum Client muss angepasst werden. Für das Verständnis von GraphQL ist es hilfreich, einige Begriffe und Konzepte zu verstehen, die im Folgenden kurz zusammengefasst werden. GraphQL besteht insgesamt aus drei Bestandteilen: Queries, einem Schema und Resolvern.

Queries

Queries bezeichnen die Abfragen in GraphQL. Die Syntax der Abfragen ähnelt der von JavaScript-Quellcode und besteht aus Funktionen und Objektstrukturen, erweitert um Typinformationen. Die ideale Umgebung für Ihre ersten Schritte mit GraphQL ist das GitHub-API. Der GraphQL-Server bietet mit dem Werkzeug GraphiQL eine grafische Schnittstelle, mit deren Hilfe Sie beispielsweise Queries entwickeln können und sofort eine Rückmeldung erhalten. Das auf GitHub als Explorer bezeichnete GraphiQL erreichen Sie unter dem URL https://developer.github.com/v4/explorer/.

Nachdem Sie sich angemeldet haben, können Sie beliebige Anfragen formulieren. Behalten Sie bei der Verwendung des Explorers stets im Kopf, dass Sie hier mit Echtdaten arbeiten. Bei lesenden Anfragen ist das noch kein Problem. GraphQL ist jedoch auch in der Lage, schreibend auf einen Server zuzugreifen. Das bedeutet, dass Sie hier mit den Berechtigungen Ihres GitHub-Nutzers Änderungen vornehmen können. Als initiales Beispiel können Sie hier die Statistiken über ein Repository auslesen. In Listing 1 sehen Sie die Anfrage.

query getRepoStats($owner: String!, $repoName: String!) {
  repository(owner:$owner, name:$repoName) {
    createdAt,
    forkCount,
    stargazers {
      totalCount
    }
    watchers {
      totalCount
    }
  }
}

Das Schlüsselwort query leitet eine lesende Abfrage ein. Anschließend folgen der Name der Abfrage sowie die Parameterliste. Im Beispiel sind das die Zeichenketten, die den Eigentümer und den Repository-Namen angeben. In der Abfrage selbst geben Sie an, dass Sie an den Informationen über die Ressource repository interessiert sind, und zwar nur für Ressourcen, die den übergebenen Kriterien entsprechen. Innerhalb dieser Funktion definieren Sie die Struktur der Antwort. Sie erhalten das Erstellungsdatum, die Anzahl der Forks, Stars und Watchers für das Repository.

Damit die Abfrage funktionieren kann, müssen Sie noch die Belegung der Variablen angeben. Das geschieht über die Sektion
Query Variables in GraphiQL. Die Variablen geben Sie in Form eines JSON-Objekts an. Danach können Sie die Anfrage absenden und erhalten eine Antwort, wie Sie sie in Abbildung 1 sehen können.

Abb. 1: Abfrage im GitHub Explorer

Abb. 1: Abfrage im GitHub Explorer

Neben den lesenden Anfragen können Sie mit GraphQL auch schreibende Anfragen erzeugen. Diese werden als Mutation bezeichnet. Ein Beispiel für eine solche Mutation kann wie folgt aussehen:

mutation {
  createTodo(todo: {
    title:"Get up",
    status: "open"
  })
}

Statt des query– wird das mutation-Schlüsselwort verwendet. Die übrige Struktur ähnelt der lesenden Anfrage bis auf die Tatsache, dass Sie innerhalb der Struktur die zu setzenden Werte übergeben.

Schema

Bei der Abfrage eines GraphQL-Servers können Sie nicht beliebige Daten in beliebiger Form abfragen, sondern müssen sich an eine gewisse Struktur halten. Diese Struktur gibt das GraphQL-Schema vor. Das Schema ist ein Baum von Objekten, die mithilfe des GraphQL-Typsystems definiert werden. Mit der Schema Definition Language (SDL) formulieren Sie die Strukturen. Hier ein einfaches Beispiel einer solchen Struktur:

type Todo {
  id: Number!
  title: String!
  status: String!
}

Als Typen erlaubt die SDL z. B. skalare Werte wie Ganz- und Fließkommazahlen, Booleans sowie Objekttypen, Interfaces, Unions, Enums und Inputs.

Resolver

GraphQL ist, wie der Name andeutet, eine Abfragesprache. Als solche besitzt sie keinerlei Wissen über die Geschäftslogik Ihrer Applikation. Als Bindeglied zwischen Ihrer Applikation und dem GraphQL-Schema fungieren die Resolver. Ein Resolver ist eine Funktion, die angibt, wie die Daten gelesen oder geschrieben werden müssen und kann sowohl für lesende als auch für schreibende Zugriffe umgesetzt werden. Um die Umsetzung möglichst flexibel zu halten, kann man Argumente an Resolver übergeben. Diese werden in der Abfrage gesetzt und können im Resolver ausgelesen werden.

Die Serverseite

Nach dieser kurzen Einführung in GraphQL kümmern wir uns im nächsten Schritt um die Implementierung der Serverseite. Mittlerweile gibt es zahlreiche Bibliotheken, die Sie bei der Erzeugung eines GraphQL-Servers unterstützen. Eine der populärsten Umsetzungen ist graphql-js, die Referenzimplementierung von Facebook in JavaScript. Aufbauend auf diesem Paket existiert express-graphql, eine Middleware für das Express-Framework, mit dem Sie auf einfache Art einen Webserver mit GraphQL-Interface erzeugen können. Die für die Serverseite benötigten Abhängigkeiten installieren Sie mit dem Kommando npm install express express-graphql graphql. Listing 2 zeigt Ihnen den Quellcode der Serverseite.

const express = require('express');
const graphqlHTTP = require('express-graphql');
const buildSchema = require('graphql').buildSchema;

const app = express();

const data = [{
  id: 1,
  title: 'Get up',
  status: 'closed'
}, {
  id: 2,
  title: 'Eat',
  status: 'open'
}];

const schema = buildSchema(`
  type Todo {
    id: Int!,
    title: String!
    status: String!
  }
  type Query {
    todo(status: String): [Todo]
  }
`);

const rootValue = {
  todo({status}) {
    return data.filter((todo) => {
      if (status) {
        return todo.status === status;
      }
      return true;
    });
  }
}

app.use('/graphql', graphqlHTTP({
  schema,
  rootValue,
  graphiql: true
}));

app.listen(8080, () => console.log('Server is listening'));

Den Rahmen des GraphQL-Servers bildet ein Express-Server. Er nutzt express-graphql als Middleware, die auf den Pfad /graphql gebunden ist. Die Verwendung der use-Funktion bindet die Middleware auf alle verfügbaren HTTP-Methoden, sodass Sie die Middleware an dieser Stelle nicht mehrfach einbinden müssen. Mit der buildSchema-Funktion des graphql-Pakets legen Sie die Struktur fest, die ein Client bei Ihrem Server abfragen kann. Die Resolver definieren Sie in Form von Methoden eines Objekts. Im Beispiel kann ein Client also eine Liste von To-dos abrufen und diese optional nach dem Status filtern. Zu diesem Zweck definieren Sie im Query-Typ in der Eigenschaft todo eine Parameterliste. Auf diese Parameter können Sie über das erste Argument der Resolver-Funktion zugreifen und die Daten entsprechend filtern. Mit der graphqlHTTP-Funktion erzeugen Sie die Middleware-Funktion, die Sie in Ihren Express-Server einbinden. Das Schema übergeben Sie als schema-Eigenschaft, die Resolver liegen in einer Eigenschaft mit dem Namen rootValue. Mithilfe der graphiql-Eigenschaft können Sie schließlich festlegen, ob Ihre Benutzer die Möglichkeit haben, das GraphiQL-Werkzeug auf Ihrem Server zu nutzen. Setzen Sie diese Eigenschaft auf den Wert true, können Sie über http://localhost:8080/graphql auf die Schnittstelle zugreifen und hier Ihre Anfragen formulieren.

Damit Sie im Folgenden Ihre Applikation mit dem Apollo-Client verbinden können, müssen Sie die cors-Middleware mit dem Kommando npm install cors installieren und im app.use-Aufruf nach der Angabe des Pfads als zusätzliche Middleware vor der graphqlHTTP-Middleware einbinden.

Der Apollo Client in React

Nun, da Sie über einen einfachen GraphQL-Server verfügen, können Sie sich der Clientseite widmen. Hier ist es zwar problemlos möglich, den Apollo Client ohne React zu verwenden. Neben React werden auch noch Angular oder Vue.js unterstützt. In diesem Beispiel kommt jedoch die bewährte Kombination aus React und dem Apollo Client zum Einsatz. Zunächst erzeugen Sie mit create-react-app eine neue React-Applikation. Das erforderliche Kommando lautet npx create-react-app todo-list. Das npx-Kommando lädt das create-react-app-Paket temporär herunter, initialisiert die Applikation und verwirft das Paket anschließend wieder. Alternativ können Sie das create-react-app-Paket auch global auf Ihrem System installieren. Mit dem Kommando yarn add apollo-boost react-apollo graphql-tag graphql installieren Sie alle Pakete, die Sie für die clientseitige Implementierung benötigen. Die Bedeutung der einzelnen Pakete ist wie folgt:

  • Apollo-boost stellt Ihnen unter anderem die ApolloClient-Klasse zur Verfügung, mit der Sie den GraphQL-Client für Ihre Applikation erzeugen können.
  • React-apollo: Damit lässt sich der Apollo Client in eine React-Applikation integrieren. Dafür exportiert dieses Paket bestimmte Komponenten.
  • Graphql-tag: Mit diesem Paket können Sie Ihre GraphQL-Anfragen erzeugen.
  • Graphql ist das GraphQL-Paket selbst.

Im ersten Schritt müssen Sie mit der ApolloClient-Klasse ein Clientobjekt erzeugen, mit dessen Hilfe Sie die Serververbindung aufbauen können, über die Sie Ihre Anfrage senden. Damit Ihnen dieser Client überall in Ihrer Applikation zur Verfügung steht, nutzt Apollo den Context Ihrer React-Applikation, um den Client dort vorzuhalten. Dafür platzieren Sie an einer möglichst zentralen Stelle, im Idealfall in der App.js-Datei, den ApolloProvider, der als Prop den Verweis auf den Client erhält. Listing 3 enthält eine angepasste Version der App.js-Datei.

import React, { Component } from 'react';

import {List} from './List';

import {ApolloProvider} from 'react-apollo';
import ApolloClient from "apollo-boost";

const client = new ApolloClient({
  uri: "http://localhost:8080/graphql"
});

class App extends Component {
  render() {
    return (
      
        
      
    );
  }
}

export default App;

Die List-Komponente ist zur Vereinfachung sowohl für das Auslesen der Daten vom GraphQL-Server als auch für die Anzeige der Informationen zuständig. In einer produktiven Applikation sollten Sie die Serverkommunikation und die Darstellung strikt getrennt halten und die Abfrage der Daten in eine Containerkomponente auslagern, die ihrerseits die Daten über Props an die Anzeigekomponente weitergibt. Die Kommunikation mit dem GraphQL-Server setzen Sie mit der Query-Komponente um. In Listing 4 sehen Sie den entsprechenden Quellcode.

import { Query } from "react-apollo";
import gql from "graphql-tag";
import React, { Component } from 'react';

export class List extends Component {

  render() {
    return (<Query query={gql`
      {
        todo {
          id
          title
          status
        }
      }
    `}>
    {({ loading, error, data }) => {
      console.log(data);
      if (loading) return <p>Loading...</p>;
      if (error) return <p>Error :(</p>;

      return (
        <ul>
          {data.todo.map((todo) => {
            return (
              <li key={todo.id} className={todo.status}>
                {todo.status === 'closed' ? '✓' : '✘'} {todo.title}
              </li>
            )
          })}
        </ul>
      )
    }}
  </Query>);
  }
}

Die Query-Komponente erhält über die query-Prop die vorformulierte Abfrage an den Server. Diese erzeugen Sie mithilfe der gql-Funktion aus dem graphql-tag-Paket. Die hier formulierte Abfrage folgt der GraphQL-Syntax, wie Sie sie auch in GraphiQL nutzen können. Das Beispiel ließe sich damit auch dahingehend erweitern, dass Sie eine Filterfunktionalität entwickeln, mit der Sie sich nur offene oder erledigte Aufgaben anzeigen lassen können. Hierfür müssen Sie lediglich die Anfrage um den status-Parameter erweitern. Innerhalb der Query-Komponente setzen Sie schließlich mit einer Callback-Funktion einen Handler um, der die Anzeige der Daten übernimmt.

Mit dieser Callback-Funktion können Sie drei Zustände abbilden: die Daten werden geladen, es ist ein Fehler aufgetreten und die Daten vom Server sind eingetroffen. Der erste Zustand spiegelt sich in der loading-Eigenschaft wider. Ist der Wert true, werden die Daten aktuell geladen. Hier können Sie Ihren Benutzern beispielsweise einen Loading Indicator anzeigen. Die error-Eigenschaft weist im Erfolgsfall den Wert undefined auf. Tritt ein Fehler auf, erhalten Sie hier eine Objektrepräsentation des Fehlers, sodass Sie entsprechend reagieren und zumindest Ihren Benutzern eine Fehlermeldung anzeigen können. Liegen noch keine Informationen vor, hat die data-Eigenschaft ein leeres Objekt als Wert.

Sobald die Daten vorliegen, können Sie über dieses Objekt darauf zugreifen. Im Beispiel haben Sie die todo-Ressource angefragt. Das data-Objekt verfügt in diesem Fall über eine Eigenschaft todo, die wiederum ein Array von To-dos enthält. Über dieses Array können Sie innerhalb Ihrer Komponente iterieren und die einzelnen Einträge in einer Liste anzeigen. Haben Sie alle Änderungen an Ihrem Quellcode nachvollzogen und den Node.js-Server mit dem Kommando node index.js und die React-Applikation mit yarn start gestartet, können Sie über http://localhost:3000 prüfen, ob Ihre Applikation soweit funktioniert. Mit ein klein wenig CSS sieht das Ergebnis wie in Abbildung 2 aus.

Abb. 2: Ergebnis Get up

Abb. 2: Ergebnis Get up

Schreibende Zugriffe mit Mutations

Der Query-Typ dient allein lesenden Zugriffen; für schreibende Zugriffe müssen Sie in Ihrem Schema den Mutation-Typ definieren. Eine der einfachsten Mutations, die in unserem Beispiel denkbar ist, ist das Ändern des Status eines To-dos. Zu diesem Zweck generieren Sie nun die toggleStatus-Mutation und binden sie anschließend in Ihre React-Applikation ein, sodass ein Benutzer beim Klick auf ein To-do dessen Status ändern kann.

Im Quellcode Ihres GraphQL-Servers sind zwei Anpassungen erforderlich, um dieses Feature umzusetzen. Zunächst müssen Sie Ihr Schema um die toggleStatus-Mutation erweitern und anschließend einen Resolver definieren, der auf den Aufruf der Mutation reagiert und den Status ändert. Die entsprechenden Anpassungen sehen wie folgt aus:

const schema = buildSchema(`
  ...
  type Mutation {
    toggleStatus(id: Int!): Todo
  }
`);

Im Schema definieren Sie den Mutation-Typ. Dieser enthält die Mutation toggleStatus, die die ID des zu bearbeitenden Datensatzes erhält. Mit dem Ausrufezeichen nach der Typangabe legen Sie fest, dass dieser Wert nicht Null sein darf, es sich also um eine Pflichtangabe handelt. Der Rückgabewert dieser Mutation ist der angepasste Datensatz, also vom Typ Todo. In der zugehörigen Resolver-Funktion suchen Sie sich zunächst den Datensatz und ändern den Status. Anschließend müssen Sie den angepassten Datensatz wieder zurück ins Array schreiben und das neue To-do zurückgeben, damit die Anforderung an den korrekten Rückgabewert erfüllt ist. Starten Sie nach diesen Änderungen Ihren Server neu, können Sie mit GraphiQL die Mutation bereits testen. Geben Sie die folgende Anfrage ein, erhalten Sie bei jeder Ausführung das Todo mit der ID 1 mit geändertem Status zurück:

mutation {
  toggleStatus(id: 1) {
    title
    status
  }
}

Nun geht es um die Integration der Mutation ins Frontend. Analog zur Query-Komponente existiert auch hier eine Mutation-Komponente. Diese findet sich ebenfalls im react-apollo-Paket. Die einzelnen Listenknoten wrappen Sie nun in Mutation-Komponenten. Als Prop übergeben Sie die ausformulierte Anfrage. Gegenüber der Anfrage, die Sie über GraphiQL abgesetzt haben, müssen Sie einige Anpassungen vornehmen. Die Anfrage soll generisch sein, damit es möglich wird, den Status eines beliebigen Listeneintrags zu ändern und nicht nur den ersten. Aus diesem Grund fügen Sie die Variable $id vom Typ Int! ein. Diese Variable können Sie dann beim Aufruf der Mutation setzen und in der Mutation selbst dann auf den Wert zugreifen. Den Inhalt der Komponente bildet eine Callback-Funktion, in der Sie Zugriff auf eine Referenz auf die Mutation in Form einer Funktion haben. Diese toggleStatus-Funktion binden Sie an das Click Event des Listeneintrags und übergeben ein Objekt mit der Eigenschaft variables, die wiederum ein Objekt mit den konkreten Variablenbelegungen enthält. Dieses Objekt verfügt über eine id-Eigenschaft, der Sie die ID des To-dos zuweisen. Den Quellcode der Mutation finden Sie in Listing 5.

<Mutation mutation={gql`
  mutation toggleStatus ($id: Int!) {
    toggleStatus(id: $id) {
      id
      title
      status
    }
  }
`}  key={todo.id} >
  {toggleStatus => (
    <li className={todo.status} onClick={() => {
      toggleStatus({ variables: { id: todo.id } });
      }}>
      {todo.status === 'closed' ? '✓' : '✘'} {todo.title}
    </li>
  )}
</Mutation>

Durch den Dateisystem-Watcher, der in den react-scripts enthalten ist, müssen Sie Ihre Applikation nicht neu laden, sondern können die Änderungen direkt im Browser testen. Klicken Sie auf einen Listeneintrag, ändert sich der Status. Diese Änderung überdauert auch einen Seiten-Reload, da die Änderungen auf dem Server persistiert sind.

Mit dem Auslesen der Statusänderung haben Sie zwei relativ einfache Anfragen an einen GraphQL-Server mit dem Apollo Client kennen gelernt. Mit dieser Grundlage sind Sie nun in der Lage, auch aufwendigere Mutationen wie beispielsweise das Erzeugen oder die Veränderung von Werten umzusetzen. Alles, was Sie hierfür tun müssen, ist ein React-Formular zu erzeugen, es in eine Mutation-Komponente zu wrappen und die Anfrage entsprechend anzupassen, damit alle Werte des Formulars übermittelt werden. Ähnlich können Sie auch beim Löschen von Einträgen vorgehen, da es sich hier ebenfalls um eine Art von Mutation handelt.

State Management

Der Apollo Client ist nicht nur in der Lage, die Kommunikation mit einem GraphQL-Server zu übernehmen, sondern kann Ihnen auch bei der Verwaltung Ihres Application State helfen. Typischerweise werden für diese Aufgabe Bibliotheken wie Redux oder MobX eingesetzt. Mit dem Apollo Client können Sie auch einen lokalen State für Ihre Applikation definieren, den Sie abfragen und modifizieren können. Zu diesem Zweck legen Sie bei der Instanziierung des Apollo Clients nicht nur den URI zum Server fest, sondern definieren außerdem eine Eigenschaft clientState. Dieses Objekt verfügt wiederum über drei Eigenschaften:

  • defaults: Ein Objekt mit der Initialbelegung des lokalen Application States
  • resolvers: Die resolvers-Eigenschaft enthält ein Objekt mit Methoden für den Zugriff auf den Application State. Diese sind analog zu den serverseitigen Resolvern und sorgen dafür, dass die Daten korrekt ausgelesen beziehungsweise die Mutation korrekt angewendet wird.
  • typeDefs: Diese Eigenschaft enthält die Beschreibung des clientseitigen Schemas.

Sie können den State Ihrer Applikation auf zwei Arten modifizieren. Entweder Sie modifizieren den State direkt. Dies erreichen Sie über die ApolloConsumer-Komponente, über die Sie Zugriff auf die Clientinstanz erhalten und mit der writeData-Methode die Informationen direkt schreiben können. Bei der Erzeugung der Anfrage in der Applikation müssen Sie dann die @client-Direktive verwenden, um dem Apollo Client zu sagen, dass er direkt in den lokalen Cache schreiben soll.

Bei lesenden Anfragen an den lokalen State gehen Sie ähnlich vor. Diese Anfragen folgen derselben Syntax wie auch die an den Server gerichteten, nur dass Sie hier nach dem Ressourcennamen ebenfalls die @client-Direktive verwenden.

Werkzeuge

Für die Arbeit mit Webapplikationen haben Sie mit den Entwicklerwerkzeugen Ihres Browsers sowie diversen Zusatzpaketen eine große Palette an Hilfsmitteln, die Ihnen die Arbeit erleichtern. Für den Apollo Client und GraphQL existieren ebenfalls etliche Werkzeuge, die Sie in Ihren Entwicklungsprozess integrieren sollten. In den folgenden Abschnitten stelle ich Ihnen mit den Apollo Client Developer Tools, dem eslint-graphql-plugin und apollo-codegen drei dieser Werkzeuge vor.

Apollo Client Developer Tools

Die Apollo Client Developer Tools sind eine Browsererweiterung, die sowohl für Firefox als auch für Chrome verfügbar ist. Mit diesem Werkzeug können Sie GraphiQL direkt aus den Entwicklertools Ihres Browsers heraus nutzen. Über die Menüpunkte Queries und Mutations erhalten Sie Zugriff auf eine Liste der Abfragen bzw. der Mutations, inklusive der jeweiligen Variablenbelegungen. Sie können hier die Struktur der Abfrage einsehen und auch über GraphiQL ausführen lassen. Über Cache erhalten Sie einen Einblick in den State Ihrer Applikation. Hier können Sie sich sowohl durch die Struktur der Queries als auch der Mutations bewegen. Die Erweiterung erhalten Sie beispielsweise über den Chrome Web Store.

Eslint-graphql-plugin

Bei der Formulierung von Anfragen schleichen sich gerne Fehler ein. Diese bemerken Sie meist erst, wenn Sie die Anfrage ausführen. Mit dem eslint-graphql-plugin [4] können Sie ein Hilfsmittel in Ihre Entwicklungsumgebung einbetten, das Ihre Abfragen anhand einer Schemadefinition überprüft. Die Schemadefinition ist das Ergebnis einer Schemaintrospektion und liegt im JSON-Format vor. Am einfachsten können Sie diese Datei mit Apollo-codegen erzeugen lassen. Welche Schritte Sie dafür unternehmen müssen, erfahren Sie im nächsten Abschnitt.

Apollo-codegen

Das Typsystem von GraphQL ist von großem Wert, da Sie anhand des Schemas ablesen können, welche Art von Wert eine bestimmte Abfrage zurückliefert. Auf der anderen Seite ist bei Mutations auch definiert, welche Typen erwartet werden. Um diese Vorteile des Typsystems auch clientseitig nutzen zu können, empfiehlt sich der Einsatz eines Typsystems für JavaScript wie TypeScript oder Flow. Damit Sie die Typdefinitionen nicht von Hand schreiben müssen, existiert mit apollo-codegen ein Werkzeug, das Ihnen die Arbeit abnimmt. Apollo-codegen können Sie mit dem Kommando npm install -g apollo-codegen global auf Ihrem Sytem installieren und es anschließend mit dem apollo-codegen-Kommando nutzen.

Im ersten Schritt benötigen Sie die Schemadefinition des GraphQL-Servers. Apollo-codegen ist in der Lage, diese entweder aus einer GraphQL-Datei oder direkt über die Serverschnittstelle zu erzeugen. Da Sie über einen lauffähigen GraphQL-Server verfügen, können Sie mit der zweiten Variante arbeiten. Mit dem Kommando apollo-codegen introspect-schema http://localhost:8081/graphql –output schema.json weisen Sie apollo-codegen an, die Schemadefinition in eine Datei mit dem Namen schema.json zu schreiben. Diese Datei benötigen Sie, wenn es um die Generierung der Typdefinitionen geht. Sie erzeugen sie mit dem Kommando apollo-codegen generate src/*.js –schema schema.json –target typescript –output graphql-types.ts für TypeScript. Für Flow müssen Sie lediglich den Wert der target-Option auf flow ändern. Der Dateiname sollte laut Konvention auf flow.js enden.

Apollo-codegen durchsucht mit diesem Befehl sämtliche JavaScript-Dateien im src-Verzeichnis nach gql-Templates und erzeugt aus ihnen Typdefinitionen. Damit apollo-codegen ordnungsgemäß funktionieren kann, dürfen Sie nur benannte Operationen verwenden. Das bedeutet, dass Sie sowohl der Query- als auch der Mutation-Operation im Quellcode einen Namen geben müssen. Das Ergebnis der Ausführung von apollo-codegen ist eine Datei mit mehreren Interfacedefinitionen, die den Abfragen aus der Applikation entsprechen. Diese Interfaces stehen Ihnen als benannte Exporte zur Verfügung, sodass Sie sie in Ihrer Applikation importieren und nutzen können.

Fazit

GraphQL bietet eine echte Alternative zu den üblicherweise im Web verwendeten REST-Schnittstellen. Der große Vorteil von GraphQL gegenüber REST ist, dass Sie die Struktur der Antwort bereits bei der Erzeugung der Anfrage bestimmen können. GraphQL erlaubt es außerdem, mehrere abhängige Ressourcen zu bündeln. Fragen Sie, wie im Beispiel, eine Liste von To-dos ab und sind außerdem an den jeweiligen Erstellern interessiert, erlaubt GraphQL, je nach Schemadefinition und Resolver-Implementierung, diese Informationen in einer Abfrage auszulesen. Sie können also tendenziell Anfragen einsparen, was sich vorteilhaft auf die Performance Ihrer Applikation sowie das zu übermittelnde Datenvolumen auswirkt. GraphQL hat jedoch nicht nur Vorteile.

Ein Nachteil, der durch die Dynamik der Abfragesprache entsteht, ist, dass die Abfragen nahezu nicht speicherbar sind, was sich negativ auf die Performance auswirkt, da sie auch nicht zwischengespeichert werden können.
GraphQL basiert auf dem HTTP-Protokoll, es ist also problemlos möglich, einen eigenen GraphQL-Client zu schreiben. Mit dem Apollo Client existiert allerdings eine sehr komfortable Implementierung, die Sie in jede beliebige Applikation einbinden können.

Der Client funktioniert mit Angular, Vue.js oder auch ganz ohne Framework. Die am weitesten verbreitete Kombination ist allerdings die mit React. Über eine Kombination aus Provider, der Ihnen die Instanz des GraphQL-Clients applikationsweit zur Verfügung stellt, und verschiedenen Komponenten können Sie an jeder Stelle in Ihrer Applikation Anfragen an den Server stellen. Da immer mehr Entwickler für die Umsetzung ihrer Applikationen auf die Kombination von Apollo Client und GraphQL setzen, gibt es auch zunehmend mehr Werkzeuge, die Sie im Entwicklungsprozess unterstützen.

PHP Magazin

Entwickler MagazinDieser Artikel ist im PHP Magazin erschienen. Das PHP Magazin deckt ein breites Spektrum an Themen ab, die für die erfolgreiche Webentwicklung unerlässlich sind.

Natürlich können Sie das PHP Magazin über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. In unserem Shop ist das Entwickler Magazin ferner im Abonnement oder als Einzelheft erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -