Deklarative Datenabfrage mit GraphQL

Avatar of Nilan Marktanner
Nilan Marktanner am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Der folgende Beitrag ist ein Gastbeitrag von Nilan Marktanner von Graph.cool. Ich weiß nicht, wie es euch geht, aber ich habe in meiner Karriere schon viel Zeit damit verbracht, mit REST-APIs zu arbeiten. Es geht immer darum, herauszufinden, welche URL man aufrufen muss, welche Daten man zurückerhält und wie man diese Daten steuern kann. Ein kurzer Blick auf GraphQL lässt es so aussehen, als ob es sowohl für die Ersteller als auch für die Verbraucher der API die Dinge vereinfacht. Lasst Nilan es erklären.

Wenn es um Standards für die Gestaltung der API-Architektur für Webdienste aller Art geht, ist REST seit vielen Jahren der Stand der Technik. Mit der weit verbreiteten Popularität von REST stieß GraphQLs Anspruch, es als API-Brücke zwischen Frontend und Backend zu ersetzen, auf viel Skepsis. Aber GraphQL ist bereits sehr gut etabliert, wie die Ankündigung von Facebook, dass GraphQL produktionsreif ist, und die Enthüllung ihrer GitHub GraphQL API durch GitHub belegen.

Die für GraphQL erforderliche Infrastruktur

Wie bei REST sind mehrere Komponenten erforderlich, bevor wir eine GraphQL-API nutzen können.

  • Ein GraphQL-Client – Dies ermöglicht uns das Abrufen oder Ändern von Daten, die im Backend gespeichert sind. Reine HTTP-Anfragen eignen sich hervorragend für kleinere Projekte, da Anfragen und Antworten beide in JSON kodiert sind.
  • Ein GraphQL-Backend – Wir müssen ein GraphQL-Schema bereitstellen, das unsere API als Typsystem beschreibt. Ein Typsystem besteht aus verschiedenen Methoden, die als Queries (zum Abrufen von Daten) und Mutations (zum Ändern von Daten) bezeichnet werden. Hier geschieht die eigentliche Arbeit, da wir eine Zuordnung zwischen diesen Methoden und unserer Datenschicht implementieren müssen. Zum Beispiel könnte eine Query `allUsers`, um alle Benutzer abzurufen, einer SQL-Abfrage wie `SELECT * FROM USERS` zugeordnet werden.

In diesem Artikel werden wir eine mögliche GraphQL-API für einen Instagram-Klon untersuchen und einige der Vorteile hervorheben, die GraphQL bietet.

Das GraphQL-Schema

Die Endpunkte einer REST-API erinnern oft an das eigentliche Schema der Datenbank. Endpunkte wie `/users` oder `/posts`, die den Zugriff auf die Datenbanktabellen `User` und `Post` ermöglichen, sind üblich.

Das GraphQL-Schema muss jedoch in einem zweistufigen Prozess als Typsystem aufgebaut werden.

Schritt 1

Objekttypen werden aus primitiven Typen wie String und Boolean zusammengesetzt. In unserem Fall bauen wir die Objekttypen `User` und `Post` auf, die in IDL-Syntax beschrieben werden

type User {
  id: String!
  name: String
  posts: [Post]
}

type Post {
  id: String!
  imageUrl: String
  description: String
  author: User
}

Wir haben einen `User`-Objekttyp, der aus den Feldern `name` vom Typ String und `posts` vom Listentyp `Post` (gekennzeichnet durch [Post]) besteht, und den `Post`-Objekttyp, der aus einem `imageUrl`- und einem `description`-Feld vom Typ String und einem `author`-Feld vom Typ `User` besteht.

Zusätzlich zu den oben genannten Feldern haben beide Objekttypen das Feld `id`, das ein erforderlicher String ist (gekennzeichnet durch `String!`).

Schritt 2

Diese Typen werden dann verwendet, um tatsächliche Queries zu definieren, wie die `allUsers`-Query, die verwendet werden kann, um alle vorhandenen Benutzer in der Datenbank abzurufen. Sie können im Grunde alles bereitstellen, was Ihnen einfällt, aber am beliebtesten sind Queries zum Abrufen aller oder eines Elements eines bestimmten Typs und sogenannte Mutationen zum Erstellen, Aktualisieren und Löschen von Elementen eines bestimmten Typs.

Werkzeuge

Das im GraphQL-Schema kodierte Typsystem ebnet den Weg für leistungsstarke Tools wie z. B. GraphiQL, das von Facebook gepflegt wird. Es ermöglicht die spielerische Erkundung einer GraphQL-API.

Funktionen wie automatische Vervollständigung (wie im GIF gezeigt) und eine automatisch generierte Dokumentation erhöhen die Entwicklererfahrung erheblich und sind in der Regel ausreichend, um mit dem Konsumieren einer GraphQL-API zu beginnen. Schauen wir uns nun einige Queries und Mutationen genauer an!

GraphQL-Queries

Queries in GraphQL sind deklarativ und hierarchisch. Wir werden gleich sehen, was das genau bedeutet, aber sich schnell ändernde Datenanforderungen in Frontend-Anwendungen machen dies zu einem der größten Verkaufsargumente von GraphQL.

Grundlagen der Query

Nehmen wir an, wir sind nur am `id` und `name` all unserer Benutzer interessiert. Deklarativ bedeutet, dass wir genau angeben, woran wir interessiert sind.

query {
  allUsers {
    id
    name
  }
}

Die Query-Antwort enthält nur die Felder, die wir gerade deklariert haben.

{
  "data": {
    "allUsers": [
      {
        "id": "some-id",
        "name": "Nilan"
      },
      {
        "id": "another-id",
        "name": "Chris"
      }
    ]
  }
}

Wir sehen, dass die Struktur der Query-Antwort eng der Struktur der Query folgt. Wenn wir mehr Daten mit einer Query abrufen möchten, fügen wir einfach mehr Felder hinzu.

query {
  allUsers {
    id
    name
    posts {
      imageUrl
    }
  }
}

Hier sehen wir, was hierarchisch bedeutet. Die Query folgt der Beziehungs-Hierarchie unseres Schemas und wir können auch die Felder auswählen, an denen wir für alle Posts interessiert sind. Die Antwort könnte lauten:

{
  "data": {
    "allUsers": [
      {
        "id": "some-id",
        "name": "Nilan",
        "posts": [
          {
            "imageUrl": "https://unsplash.it/200/300?image=31"
          },
          {
            "imageUrl": "https://unsplash.it/200/300?image=38"
          }
        ]
      },
      {
        "id": "another-id",
        "name": "Chris",
        "posts": [
          {
            "imageUrl": "https://unsplash.it/200/300?image=99"
          }
        ]
      }
    ]
  }
}

Es ist auch wichtig zu beachten, dass wir die Query einfach geändert haben, ohne unser GraphQL-Backend anzufassen, und es funktionierte immer noch! Sobald wir über ein solides Typsystem nachgedacht und dieses in unserem GraphQL-Schema bereitgestellt haben, können wir Queries in unseren Frontends im Handumdrehen ändern, und unser GraphQL-Server gibt einfach die richtige Antwort zurück. Das bedeutet auch kein lästiges Herumhantieren mit mehreren Endpunkten oder API-Versionen mehr, wie es bei REST üblich ist.

Das hierarchische Kombinieren von Feldern, wie wir es getan haben, reduziert auch die Anzahl der HTTP-Anfragen zwischen Frontend und Backend. Das Abrufen eines Benutzers und all seiner Freunde erfordert normalerweise mindestens zwei Anfragen mit REST, während wir nur eine benötigten.

Erweiterte Query-Funktionen

GraphQL-Queries akzeptieren Query-Parameter, die normalerweise verwendet werden, um erweiterte Funktionen wie Filterung oder Paginierung anzubieten. Wir werden hier nicht ins Detail gehen, aber lassen Sie uns ein paar schnelle Beispiele sehen.

Die Filterung ist ziemlich flexibel und leistungsstark. Zum Beispiel können wir nur Benutzer mit dem Namen `Chris` anzeigen.

query {
  allUsers(filter: {name: "Chris"}) {
    id
    name
  }
}

Überraschenderweise enthält die Query-Antwort nur einen Benutzer.

{
  "data": {
    "allUsers": [
      {
        "id": "another-id",
        "name": "Chris"
      }
    ]
  }
}

Mit Paginierung können wir ausdrücken, wie viele aufeinanderfolgende Datenelemente wir interessieren. Dies ist besonders nützlich, wenn wir einen Feed mit mehreren Seiten erstellen möchten, ähnlich wie bei Google-Suchergebnissen.

Lassen Sie uns nur den ersten unserer Benutzer abfragen. Wir können das Argument `first` verwenden, um dies zu erreichen.

query {
  allUsers(first: 1) {
    id
    name
  }
}

Und wie erwartet erhalten wir nur den ersten Benutzer zurück.

{
  "data": {
    "allUsers": [
      {
        "id": "some-id",
        "name": "Nilan"
      }
    ]
  }
}

Wenn wir nun den zweiten Benutzer abfragen möchten, können wir den ersten Benutzer überspringen und nur den nächsten Benutzer abrufen, indem wir `first` mit `skip` kombinieren.

query {
  allUsers(first: 1, skip: 1) {
    id
    name
  }
}

Dies gibt den zweiten Benutzer zurück.

{
  "data": {
    "allUsers": [
      {
        "id": "another-id",
        "name": "Chris"
      }
    ]
  }
}

Dies sind nur einige der Möglichkeiten, wie wir Query-Argumente verwenden können. Wenn wir eine weitere ähnliche Funktionalität bereitstellen möchten, wie z. B. das Sortieren der Query-Antwort nach einem Feld, müssten wir ein neues Query-Argument im GraphQL-Schema definieren und diese Funktion im Backend entsprechend implementieren.

Mutationen

Mutationen sind das Gegenstück zu Queries. Während wir mit Queries Daten abrufen können, ermöglichen Mutationen das Erstellen neuer Daten oder das Aktualisieren oder Löschen vorhandener Datenelemente. Die entsprechenden Mutationen könnten `createPost`, `updatePost` und `deletePost` heißen.

Die Werte für die Felder des neuen Datenelements werden mit Query-Parametern übergeben. Mutationen erfordern auch die Auswahl von Feldern, die als Query-Antwort zurückgegeben werden. Eine Mutation zum Erstellen eines neuen Posts könnte so aussehen:

mutation {
  createPost(imageUrl: "https://unsplash.it/200/300?image=27", description: "#random", authorId: "some-id") {
    id
  }
}

Die Antwort enthält die neu erstellte `id`.

{
  "data": {
    "createPost": {
      "id": "some-newly-generated-id"
    }
  }
}

Fazit

In diesem Artikel haben wir einen Überblick über GraphQL als Alternative zu REST erhalten.

Wir haben gesehen, dass GraphQL-Clients benötigt werden, um das Frontend mit dem Backend zu verbinden, und wie ein GraphQL-Schema für einen Instagram-Klon aussehen kann. Wir haben gelernt, wie wir Queries und Mutationen zum Abrufen und Ändern von Daten verwenden können, und die GraphQL-API ermöglicht es uns, genau die Felder auszuwählen, die wir in einer deklarativen und hierarchischen Weise benötigen. Mit Filterung und Paginierung haben wir zwei leistungsstarke Konzepte gestreift, während GraphiQL die fantastischen Möglichkeiten von Tools gezeigt hat, die auf dem definierten Typsystem basieren.

Wenn Sie mehr über GraphQL erfahren möchten, sollten Sie sich den „Learn“-Bereich der GraphQL-Website und Learn GraphQL ansehen, beides sind sehr zugängliche Ressourcen.

Relay und Apollo Client sind zwei beliebte GraphQL-Clients für JavaScript. Wenn Sie an Relay interessiert sind, besuchen Sie Learn Relay für eine interaktive und umfassende Einführung in Relay.

graphql-js und Sangria sind zwei beliebte Bibliotheken zum Erstellen eines GraphQL-Backends in JavaScript bzw. Scala.

Um schnell mit GraphQL loszulegen, können Sie Graphcool ausprobieren, das es Ihnen ermöglicht, Ihre Modelle und Felder grafisch zu definieren, um automatisch ein GraphQL-Backend zu generieren, und zusätzliche Funktionen wie ein erweitertes Berechtigungssystem und Dateiverwaltung sofort bietet.