Was ist GraphQL?

Zexuan Luo

Zexuan Luo

November 4, 2022

Technology

Was ist GraphQL? Wie beliebt ist es?

GraphQL ist eine API-orientierte Abfragemanipulationssprache, die 2015 von Facebook veröffentlicht wurde. Im Gegensatz zu anderen API-Designs ermöglicht GraphQL Clients, Abfrageanweisungen basierend auf einer vorab vereinbarten Datenstruktur zu formulieren, und ermöglicht es dem Server, die Anweisung zu analysieren und nur das zurückzugeben, was benötigt wird. Auf diese Weise bietet GraphQL Reichhaltigkeit und Flexibilität, während gleichzeitig der Leistungsverlust durch redundante Daten vermieden wird, was GraphQL zu einer hervorragenden Wahl für Anwendungen macht, die mit vielen komplexen Datenobjekten umgehen müssen.

Im Jahr 2018 veröffentlichte GraphQL eine vollständige Spezifikation und eine stabile Version. Im selben Jahr spendete Facebook das GraphQL-Projekt an die GraphQL Foundation unter der Linux Foundation. Seitdem hat GraphQL in vielen Open-Source-Projekten und kommerziellen Organisationen Einzug gehalten. Bis heute gibt es mehrere bedeutende clientseitige Implementierungen von GraphQL auf dem Markt. Serverseitige Implementierungen sind in allen gängigen serverseitigen Programmiersprachen verfügbar und sogar in Nischensprachen wie D und R.

Einige reale Szenarien und Herausforderungen für GraphQL

Das bekannteste Beispiel für GraphQL ist die GraphQL-API von GitHub.

Bevor GitHub GraphQL übernahm, stellte es eine REST API bereit, um die reichhaltigen Daten, die von Millionen gehosteter Projekte generiert wurden, verfügbar zu machen. Diese war so erfolgreich, dass sie zum Vorbild für die Gestaltung von REST-APIs wurde. Als jedoch die Anzahl der Datenobjekte und die Felder innerhalb der Objekte wuchsen, begannen sich immer mehr Nachteile der REST-API zu zeigen. Auf der Serverseite musste GitHub strenge Grenzen für die Häufigkeit der Aufrufe setzen, um die Kosten aufgrund der Datenmenge, die mit jedem Aufruf generiert wurde, zu reduzieren. Auf der Entwicklerseite mussten sie sich mit dieser Einschränkung auseinandersetzen, da ein einzelner Aufruf zwar viele Daten zurückgab, der Großteil davon jedoch nutzlos war. Um eine bestimmte Information zu erhalten, mussten Entwickler oft mehrere Abfragen starten und dann viel Klebcode schreiben, um die sinnvollen Daten aus den Abfrageergebnissen zu dem gewünschten Inhalt zusammenzufügen. Dabei mussten sie sich auch den Fesseln der "Anzahl der Aufrufe" unterwerfen.

Daher hat GitHub GraphQL sofort übernommen, sobald es verfügbar war. GitHub wurde zum "Botschafter" von GraphQL und brachte seine Anwendung Tausenden von Entwicklern nahe. Die GraphQL-API ist mittlerweile die erste Wahl für GitHub. Seit der ersten Ankündigung der Unterstützung für GraphQL hat GitHub jedes Jahr mehrere Artikel über GraphQL veröffentlicht. Um Entwicklern die Migration zu GraphQL zu erleichtern, hat GitHub eine interaktive Abfrageanwendung speziell für diesen Zweck geschrieben: https://docs.github.com/en/graphql/overview/explorer. Entwickler können durch diese Anwendung lernen, wie man GraphQL schreibt.

Allerdings ist GraphQL kein Allheilmittel. Erst kürzlich hat GitHub seine eigene GraphQL-Implementierung der Paket-API eingestellt. Viele Menschen haben auch begonnen, einige der Nachteile von GraphQL zu diskutieren. Viele der Probleme mit GraphQL rühren daher, dass seine Struktur so anders ist als die des HTTP-Standards, dass es keine einfache Möglichkeit gibt, einige der Konzepte von GraphQL in eine Struktur wie den HTTP-Pfad/Header abzubilden. GraphQL als normale HTTP-API zu behandeln, erfordert zusätzliche Entwicklungsarbeit. Daher müssen Entwickler, die ihre eigenen GraphQL-APIs verwalten möchten, ein GraphQL-fähiges API-Gateway verwenden.

Wie APISIX GraphQL unterstützt

Derzeit unterstützt APISIX das dynamische Routing durch einige Eigenschaften von GraphQL. Mit dieser Fähigkeit können wir nur bestimmte GraphQL-Anfragen akzeptieren oder verschiedene GraphQLs an verschiedene Upstreams weiterleiten.

Nehmen wir die folgende GraphQL-Anweisung als Beispiel:

query getRepo {
    owner {
        name
    }
    repo {
        created
    }
}

APISIX extrahiert die folgenden drei Eigenschaften von GraphQL für das Routing:

  • graphql_operation
  • graphql_name
  • graphql_root_fields

In der obigen GraphQL-Anweisung:

  • graphql_operation entspricht query
  • graphql_name entspricht getRepo
  • graphql_root_fields entspricht ["owner", "repo"]

Lassen Sie uns eine Route erstellen, um die fein abgestimmten Routing-Fähigkeiten von APISIX für GraphQL zu demonstrieren:

curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
    "methods": ["POST"],
    "uri": "/graphql",
    "vars": [
        ["graphql_operation", "==", "query"],
        ["graphql_name", "==", "getRepo"],
        ["graphql_root_fields", "has", "owner"]
    ],
    "upstream": {
        "type": "roundrobin",
        "nodes": {
            "127.0.0.1:2022": 1
        }
    }
}'

Als nächstes verwenden wir eine Anfrage mit einer GraphQL-Anweisung:

curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
    owner {
        name
    }
    repo {
        created
    }
}'
HTTP/1.1 200 OK
...

Wir können sehen, dass die Anfrage den Upstream erreicht hat, da die Abfrageanweisung alle drei Bedingungen erfüllt.

Umgekehrt, wenn wir mit einer nicht übereinstimmenden Anweisung zugreifen, zum Beispiel, wenn das Feld owner nicht enthalten ist:

curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
    repo {
        created
    }
}'
HTTP/1.1 404 Not Found
...

Es wird nicht der entsprechenden Routing-Regel entsprechen.

Wir können zusätzlich eine Route erstellen, die Anweisungen, die kein owner-Feld enthalten, an einen anderen Upstream weiterleitet:

curl http://127.0.0.1:9180/apisix/admin/routes/2 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
    "methods": ["POST"],
    "uri": "/graphql",
    "vars": [
        ["graphql_operation", "==", "query"],
        ["graphql_name", "==", "getRepo"],
        ["graphql_root_fields", "!", "has", "owner"]
    ],
    "upstream": {
        "type": "roundrobin",
        "nodes": {
            "192.168.0.1:2022": 1
        }
    }
}'
curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
    repo {
        created
    }
}'
HTTP/1.1 200 OK
...

Aussichten für die zukünftige Unterstützung von GraphQL durch APISIX

Neben dem dynamischen Routing könnte APISIX in Zukunft auch mehr Operationen basierend auf spezifischen Feldern von GraphQL einführen. Zum Beispiel hat die GraphQL-API von GitHub eine spezifische Formel für die Ratenbegrenzung, und wir können ähnliche Regeln anwenden, um eine einzelne GraphQL-Anfrage in eine entsprechende Anzahl von "virtuellen Aufrufen" umzuwandeln, um eine GraphQL-spezifische Ratenbegrenzung zu erreichen.

Wir können das Problem auch anders betrachten. Die Anwendung selbst stellt weiterhin die REST-API bereit, und das Gateway wandelt GraphQL-Anfragen in REST-Anfragen und REST-Antworten in GraphQL-Antworten auf der äußersten Ebene um. Die auf diese Weise bereitgestellte GraphQL-API kann Funktionen wie RBAC, Ratenbegrenzung, Caching usw. ausführen, ohne spezielle Plugins entwickeln zu müssen. Aus technischer Sicht ist diese Idee nicht allzu schwer umzusetzen. Schließlich neigen selbst REST-APIs im Jahr 2022 dazu, OpenAPI-Spezifikationen als Schema bereitzustellen, was lediglich eine Übertragung zwischen GraphQL-Schema und OpenAPI-Schema ist, plus GraphQL-spezifische Feldfilterung. (Natürlich muss ich zugeben, dass ich es selbst noch nicht ausprobiert habe. Vielleicht gibt es in einigen Details Herausforderungen, die noch überwunden werden müssen.)

Aufmerksame Leser werden feststellen, dass die auf diese Weise konvertierten GraphQL-APIs nur mit einem Modell gleichzeitig arbeiten können, was offensichtlich nicht den Flexibilitätsanforderungen von GraphQL entspricht und nichts anderes als eine REST-API in GraphQL-Gewand ist. Allerdings gibt es in GraphQL ein Konzept namens Schema Stitching, das es Implementierern ermöglicht, mehrere Schemata zusammenzuführen.

Als Beispiel haben wir zwei APIs, eine namens GetEvent und die andere namens GetLocation, die die Typen Event und Location zurückgeben.

type Event {
    id: string
    location_id: string
}

type Location {
    id: string
    city: string
}

type Query {
    GetEvent(id: string): Event
    GetLocation(id: string): Location
}

Wir können eine Konfiguration hinzufügen, die diese beiden APIs in eine neue API namens GetEventWithLocation kombiniert, die wie folgt aussieht:

type EventWithLocation {
    id: string
    location: Location
}

type Query {
    GetEventWithLocation(id: string): EventWithLocation
}

Die spezifische Implementierung des Stitchings wird vom Gateway übernommen. Im obigen Beispiel teilt das Gateway die API in zwei Teile auf, ruft GetEvent auf, um die location_id zu erhalten, und dann GetLocation, um die kombinierten Daten zu erhalten.

Kurz gesagt, durch die Umwandlung von REST in GraphQL kann jede REST-API in ein entsprechendes GraphQL-Modell umgewandelt werden; und mit Hilfe von Schema Stitching können mehrere Modelle zu einer GraphQL-API kombiniert werden. Auf diese Weise können wir eine reichhaltige und flexible GraphQL-API auf der bestehenden REST-API aufbauen und spezifische Plugins auf der Granularität der REST-API verwalten. Dieses Design löst nebenbei einige der API-Orchestrierungsprobleme. Wie im obigen Beispiel nehmen wir die Ausgabe einer API (Event.location_id) als Eingabe einer anderen API (Location.id).

Tags: