So erstellen Sie eine Echtzeit-App mit GraphQL-Abonnements auf Postgres
Veröffentlicht: 2022-03-10In diesem Artikel werfen wir einen Blick auf die Herausforderungen beim Erstellen von Echtzeitanwendungen und darauf, wie aufkommende Tools sie mit eleganten Lösungen angehen, die leicht zu begründen sind. Dazu erstellen wir eine Echtzeit-Umfrage-App (wie eine Twitter-Umfrage mit Gesamtstatistiken in Echtzeit), indem wir einfach Postgres, GraphQL, React und keinen Backend-Code verwenden!
Das Hauptaugenmerk liegt dabei auf der Einrichtung des Backends (Bereitstellung der gebrauchsfertigen Tools, Schemamodellierung) und Aspekten der Frontend-Integration mit GraphQL und weniger auf UI/UX des Frontends (einige Kenntnisse von ReactJS sind hilfreich). Der Tutorial-Abschnitt verfolgt einen Paint-by-Numbers-Ansatz, also klonen wir einfach ein GitHub-Repository für die Schemamodellierung und die Benutzeroberfläche und optimieren es, anstatt die gesamte App von Grund auf neu zu erstellen.
Alles rund um GraphQL
Wissen Sie alles, was Sie über GraphQL wissen müssen? Wenn Sie Zweifel haben, hat Eric Baer Sie mit einer detaillierten Anleitung zu seinen Ursprüngen, seinen Nachteilen und den Grundlagen der Arbeit damit versorgt. Lesen Sie einen verwandten Artikel →
Bevor Sie diesen Artikel weiterlesen, möchte ich erwähnen, dass praktische Kenntnisse der folgenden Technologien (oder Ersatzstoffe) von Vorteil sind:
- ReagierenJS
Dies kann durch ein beliebiges Front-End-Framework, Android oder IOS ersetzt werden, indem Sie der Dokumentation der Client-Bibliothek folgen. - Postgres
Sie können mit anderen Datenbanken arbeiten, aber mit anderen Tools gelten die in diesem Beitrag beschriebenen Prinzipien weiterhin.
Sie können diesen Tutorial-Kontext auch sehr einfach für andere Echtzeit-Apps anpassen.

Wie die begleitende GraphQL-Nutzlast unten zeigt, gibt es drei Hauptfunktionen, die wir implementieren müssen:
- Rufen Sie die Umfragefrage und eine Liste mit Optionen ab (oben links).
- Ermöglichen Sie einem Benutzer, für eine bestimmte Umfragefrage abzustimmen (die Schaltfläche „Abstimmen“).
- Rufen Sie die Ergebnisse der Umfrage in Echtzeit ab und zeigen Sie sie in einem Balkendiagramm an (oben rechts; wir können die Funktion zum Abrufen einer Liste der derzeit online Benutzer beschönigen, da es sich um eine exakte Nachbildung dieses Anwendungsfalls handelt).
Herausforderungen beim Erstellen von Echtzeit-Apps
Das Erstellen von Echtzeit-Apps (insbesondere als Frontend-Entwickler oder jemand, der kürzlich den Übergang zum Fullstack-Entwickler vollzogen hat) ist ein schwer zu lösendes technisches Problem. So funktionieren moderne Echtzeit-Apps im Allgemeinen (im Kontext unserer Beispiel-App):
- Das Frontend aktualisiert eine Datenbank mit einigen Informationen; Die Stimme eines Benutzers wird an das Backend gesendet, dh Umfrage/Option und Benutzerinformationen (
user_id
,option_id
). - Das erste Update löst einen anderen Dienst aus, der die Umfragedaten aggregiert, um eine Ausgabe zu rendern, die in Echtzeit an die App zurückgesendet wird (jedes Mal, wenn eine neue Stimme von irgendjemandem abgegeben wird; wenn dies effizient geschieht, werden nur die aktualisierten Umfragedaten verarbeitet und Nur die Clients, die diese Umfrage abonniert haben, werden aktualisiert):
- Abstimmungsdaten werden zuerst von einem
register_vote
-Dienst verarbeitet (angenommen, dass hier eine Validierung stattfindet), der einenpoll_results
-Dienst auslöst. - Aggregierte Umfragedaten in Echtzeit werden vom
poll_results
-Dienst an das Frontend weitergeleitet, um Gesamtstatistiken anzuzeigen.
- Abstimmungsdaten werden zuerst von einem

Dieses Modell ist von einem traditionellen API-Building-Ansatz abgeleitet und hat folglich ähnliche Probleme:
- Jeder der aufeinanderfolgenden Schritte könnte schief gehen, wodurch die UX hängen bleibt und andere unabhängige Vorgänge beeinträchtigt werden.
- Erfordert viel Aufwand auf der API-Ebene, da es sich um einen einzigen Kontaktpunkt für die Frontend-App handelt, die mit mehreren Diensten interagiert. Es muss auch eine Websockets-basierte Echtzeit-API implementieren – es gibt keinen universellen Standard dafür und sieht daher nur begrenzte Unterstützung für die Automatisierung in Tools.
- Die Front-End-App muss die erforderlichen Installationen hinzufügen, um die Echtzeit-API zu nutzen, und muss möglicherweise auch das Datenkonsistenzproblem lösen, das typischerweise bei Echtzeit-Apps auftritt (weniger wichtig in unserem gewählten Beispiel, aber kritisch beim Ordnen von Nachrichten in einer echten -Time-Chat-App).
- Viele Implementierungen greifen auf die Verwendung zusätzlicher nicht relationaler Datenbanken auf der Serverseite (Firebase usw.) zurück, um eine einfache Echtzeit-API-Unterstützung zu gewährleisten.
Werfen wir einen Blick darauf, wie GraphQL und die zugehörigen Tools diese Herausforderungen angehen.
Was ist GraphQL?
GraphQL ist eine Spezifikation für eine Abfragesprache für APIs und eine serverseitige Laufzeit zum Ausführen von Abfragen. Diese Spezifikation wurde von Facebook entwickelt, um die App-Entwicklung zu beschleunigen und ein standardisiertes, datenbankunabhängiges Datenzugriffsformat bereitzustellen. Jeder spezifikationskonforme GraphQL-Server muss Folgendes unterstützen:
- Abfragen für Lesevorgänge
Ein Anforderungstyp zum Anfordern verschachtelter Daten von einer Datenquelle (die entweder eine oder eine Kombination aus einer Datenbank, einer REST-API oder einem anderen GraphQL-Schema/Server sein kann). - Mutationen für Schreibvorgänge
Ein Anfragetyp zum Schreiben/Weiterleiten von Daten in die oben genannten Datenquellen. - Abonnements für Live-Abfragen
Ein Anforderungstyp für Clients zum Abonnieren von Echtzeit-Updates.
GraphQL verwendet auch ein typisiertes Schema. Das Ökosystem verfügt über zahlreiche Tools, die Ihnen helfen, Fehler zur Entwicklungs-/Kompilierungszeit zu identifizieren, was zu weniger Laufzeitfehlern führt.
Deshalb eignet sich GraphQL hervorragend für Echtzeit-Apps:
- Live-Abfragen (Abonnements) sind ein impliziter Bestandteil der GraphQL-Spezifikation. Jedes GraphQL-System muss über native Echtzeit-API-Funktionen verfügen.
- Eine Standardspezifikation für Echtzeitabfragen hat die Bemühungen der Community um clientseitige Tools konsolidiert, was zu einer sehr intuitiven Art der Integration mit GraphQL-APIs geführt hat.
GraphQL und eine Kombination aus Open-Source-Tools für Datenbankereignisse und serverlosen/Cloud-Funktionen bieten ein hervorragendes Substrat für die Erstellung von Cloud-nativen Anwendungen mit asynchroner Geschäftslogik und Echtzeitfunktionen, die einfach zu erstellen und zu verwalten sind. Dieses neue Paradigma führt auch zu einer großartigen Benutzer- und Entwicklererfahrung.
Im Rest dieses Artikels werde ich Open-Source-Tools verwenden, um eine App basierend auf diesem Architekturdiagramm zu erstellen:

Erstellen einer Umfrage-/Abstimmungs-App in Echtzeit
Lassen Sie uns nach dieser Einführung in GraphQL wieder zum Erstellen der Umfrage-App zurückkehren, wie im ersten Abschnitt beschrieben.
Die drei Funktionen (oder hervorgehobenen Geschichten) wurden ausgewählt, um die verschiedenen GraphQL-Anforderungstypen zu demonstrieren, die unsere App stellen wird:
- Anfrage
Rufen Sie die Umfragefrage und ihre Optionen ab. - Mutation
Lassen Sie einen Benutzer eine Stimme abgeben. - Abonnement
Zeigen Sie ein Echtzeit-Dashboard für Umfrageergebnisse an.

Voraussetzungen
- Ein Heroku-Konto (verwenden Sie die kostenlose Stufe, keine Kreditkarte erforderlich)
Zum Bereitstellen eines GraphQL-Backends (siehe nächster Punkt unten) und einer Postgres-Instanz. - Hasura GraphQL Engine (kostenlos, Open Source) Ein gebrauchsfertiger GraphQL-Server auf Postgres.
- Apollo Client (kostenloses Open-Source-SDK)
Zur einfachen Integration von Client-Apps mit einem GraphQL-Server. - npm (kostenloser Open-Source-Paketmanager)
Um unsere React-App auszuführen.
Bereitstellen der Datenbank und eines GraphQL-Backends
Wir werden jeweils eine Instanz von Postgres und GraphQL Engine auf dem kostenlosen Kontingent von Heroku bereitstellen. Wir können eine raffinierte Heroku-Schaltfläche verwenden, um dies mit einem einzigen Klick zu tun.

Hinweis: Sie können auch diesem Link folgen oder nach der Dokumentation Hasura GraphQL-Bereitstellung für Heroku (oder andere Plattformen) suchen.

Sie benötigen keine zusätzliche Konfiguration und können einfach auf die Schaltfläche „App bereitstellen“ klicken. Notieren Sie sich nach Abschluss der Bereitstellung die App-URL:
<app-name>.herokuapp.com
Im Screenshot oben wäre es zum Beispiel:
hge-realtime-app-tutorial.herokuapp.com
Bisher haben wir eine Instanz von Postgres (als Add-On im Heroku-Jargon) und eine Instanz der GraphQL-Engine bereitgestellt, die für die Verwendung dieser Postgres-Instanz konfiguriert ist. Als Ergebnis haben wir jetzt eine gebrauchsfertige GraphQL-API, aber da wir keine Tabellen oder Daten in unserer Datenbank haben, ist dies noch nicht nützlich. Lassen Sie uns dies also sofort ansprechen.
Modellierung des Datenbankschemas
Das folgende Schemadiagramm zeigt ein einfaches relationales Datenbankschema für unsere Umfrage-App:

Wie Sie sehen können, ist das Schema ein einfaches, normalisiertes Schema, das Fremdschlüsseleinschränkungen nutzt. Es sind diese Beschränkungen, die von der GraphQL-Engine als 1:1- oder 1:viele-Beziehungen interpretiert werden (z. B. poll:options
ist eine 1:viele-Beziehung, da jede Umfrage mehr als 1 Option hat, die durch die Fremdschlüssel-Beschränkung zwischen den verknüpft sind id
-Spalte der poll
-Tabelle und die poll_id
Spalte in der option
). Zugehörige Daten können als Diagramm modelliert werden und somit eine GraphQL-API unterstützen. Genau das macht die GraphQL Engine.

Basierend auf dem oben Gesagten müssen wir die folgenden Tabellen und Einschränkungen erstellen, um unser Schema zu modellieren:
-
Poll
Eine Tabelle zum Erfassen der Umfragefrage. -
Option
Optionen für jede Umfrage. -
Vote
Um die Stimme eines Benutzers aufzuzeichnen. - Fremdschlüsseleinschränkung zwischen den folgenden Feldern (
table : column
):-
option : poll_id → poll : id
-
vote : poll_id → poll : id
-
vote : created_by_user_id → user : id
-
Nachdem wir nun unser Schemadesign haben, implementieren wir es in unserer Postgres-Datenbank. Um dieses Schema sofort aufzurufen, gehen wir wie folgt vor:
- Laden Sie die GraphQL-Engine-CLI herunter.
- Klonen Sie dieses Repo:
$ git clone clone https://github.com/hasura/graphql-engine $ cd graphql-engine/community/examples/realtime-poll
- Gehen Sie zu
hasura/
und bearbeitenconfig.yaml
:endpoint: https://<app-name>.herokuapp.com
- Wenden Sie die Migrationen mit der CLI aus dem Projektverzeichnis an (das Sie gerade durch Klonen heruntergeladen haben):
$ hasura migrate apply
Das war's für das Backend. Sie können jetzt die Konsole der GraphQL-Engine öffnen und überprüfen, ob alle Tabellen vorhanden sind (die Konsole ist unter https://<app-name>.herokuapp.com/console
).
Hinweis: Sie hätten auch die Konsole verwenden können, um das Schema zu implementieren, indem Sie einzelne Tabellen erstellt und dann mithilfe einer Benutzeroberfläche Einschränkungen hinzugefügt haben. Die Verwendung der integrierten Unterstützung für Migrationen in der GraphQL-Engine ist nur eine praktische Option, die verfügbar war, da unser Beispiel-Repository Migrationen zum Aufrufen der erforderlichen Tabellen und zum Konfigurieren von Beziehungen/Einschränkungen enthält (dies wird auch dringend empfohlen, unabhängig davon, ob Sie ein Hobby aufbauen Projekt oder eine produktionsreife App).
Integrieren der Frontend-React-App in das GraphQL-Backend
Das Frontend in diesem Tutorial ist eine einfache App, die die Umfragefrage, die Option zum Abstimmen und die aggregierten Umfrageergebnisse an einem Ort anzeigt. Wie ich bereits erwähnt habe, konzentrieren wir uns zunächst auf die Ausführung dieser App, damit Sie sofort von der Verwendung unserer kürzlich bereitgestellten GraphQL-API profitieren. Sehen Sie sich an, wie die GraphQL-Konzepte, die wir weiter oben in diesem Artikel betrachtet haben, die verschiedenen Anwendungsfälle einer solchen App unterstützen , und erkunden Sie dann, wie die GraphQL-Integration unter der Haube funktioniert.
HINWEIS: Wenn Sie neu bei ReactJS sind, möchten Sie vielleicht einige dieser Artikel lesen. Wir werden nicht auf die Details des React-Teils der App eingehen und uns stattdessen mehr auf die GraphQL-Aspekte der App konzentrieren. Sie können sich auf den Quellcode im Repository beziehen, um Einzelheiten darüber zu erfahren, wie die React-App erstellt wurde .
Konfigurieren der Frontend-App
- Bearbeiten Sie in dem im vorherigen Abschnitt geklonten Repo
HASURA_GRAPHQL_ENGINE_HOSTNAME
in der Datei src/apollo.js (im Ordner/community/examples/realtime-poll
) und legen Sie sie auf die Heroku-App-URL von oben fest:export const HASURA_GRAPHQL_ENGINE_HOSTNAME = 'random-string-123.herokuapp.com';
- Gehen Sie zum Stammverzeichnis des Repository/App-Ordners (
/realtime-poll/
) und verwenden Sie npm, um die erforderlichen Module zu installieren, und führen Sie dann die App aus:$ npm install $ npm start

Sie sollten jetzt mit der App herumspielen können. Stimmen Sie so oft ab, wie Sie möchten, Sie werden feststellen, dass sich die Ergebnisse in Echtzeit ändern. Wenn Sie eine andere Instanz dieser Benutzeroberfläche einrichten und sie auf dasselbe Back-End verweisen, können Sie die über alle Instanzen hinweg aggregierten Ergebnisse sehen.
Wie verwendet diese App GraphQL? Weiter lesen.
Hinter den Kulissen: GraphQL
In diesem Abschnitt untersuchen wir die GraphQL-Funktionen, die die App unterstützen, gefolgt von einer Demonstration der einfachen Integration im nächsten Abschnitt.
Die Umfragekomponente und das aggregierte Ergebnisdiagramm
Die Umfragekomponente oben links, die eine Umfrage mit all ihren Optionen abruft und die Stimme eines Benutzers in der Datenbank erfasst. Beide Vorgänge werden mithilfe der GraphQL-API ausgeführt. Um die Details einer Umfrage abzurufen, stellen wir eine Abfrage (erinnern Sie sich an diese aus der GraphQL-Einführung?):
query { poll { id question options { id text } } }
Mit der Mutation-Komponente von „ react-apollo
“ können wir die Mutation mit einem HTML-Formular verbinden, sodass die Mutation mit den Variablen optionId
und userId
“ ausgeführt wird, wenn das Formular gesendet wird:
mutation vote($optionId: uuid!, $userId: uuid!) { insert_vote(objects: [{option_id: $optionId, created_by_user_id: $userId}]) { returning { id } } }
Um die Umfrageergebnisse anzuzeigen, müssen wir die Anzahl der Stimmen pro Option aus den Daten in der Abstimmungstabelle ableiten. Wir können eine Postgres-Ansicht erstellen und sie mit der GraphQL-Engine verfolgen, um diese abgeleiteten Daten über GraphQL verfügbar zu machen.
CREATE VIEW poll_results AS SELECT poll.id AS poll_id, o.option_id, count(*) AS votes FROM (( SELECT vote.option_id, option.poll_id, option.text FROM ( vote LEFT JOIN public.option ON ((option.id = vote.option_id)))) o LEFT JOIN poll ON ((poll.id = o.poll_id))) GROUP BY poll.question, o.option_id, poll.id;
Die Ansicht poll_results
Daten aus vote
und poll
zusammen, um eine aggregierte Anzahl von Stimmen für jede Option bereitzustellen.
Mithilfe von GraphQL-Abonnements über dieser Ansicht, „react-google-charts“ und der Abonnementkomponente von „ react-apollo
“ können wir ein reaktives Diagramm erstellen, das in Echtzeit aktualisiert wird, wenn eine neue Abstimmung von einem beliebigen Client erfolgt.
subscription getResult($pollId: uuid!) { poll_results(where: {poll_id: {_eq: $pollId}}) { option { id text } votes } }
GraphQL-API-Integration
Wie ich bereits erwähnt habe, habe ich Apollo Client, ein Open-Source-SDK, verwendet, um eine ReactJS-App in das GraphQL-Backend zu integrieren. Apollo Client ist analog zu jeder HTTP-Client-Bibliothek wie Anfragen für Python, das Standard-HTTP-Modul für JavaScript und so weiter. Es kapselt die Details einer HTTP-Anforderung (in diesem Fall POST-Anforderungen). Es verwendet die Konfiguration (angegeben in src/apollo.js
), um Abfrage-/Mutations-/Abonnementanforderungen (angegeben in src/GraphQL.jsx mit der Option, Variablen zu verwenden, die im JavaScript-Code Ihrer REACT-App dynamisch ersetzt werden können) zu stellen ein GraphQL-Endpunkt. Es nutzt auch das typisierte Schema hinter dem GraphQL-Endpunkt, um eine Validierung der Kompilierungs-/Entwicklungszeit für die oben genannten Anforderungen bereitzustellen. Sehen wir uns an, wie einfach es für eine Client-App ist, eine Live-Anfrage (Abonnement) an die GraphQL-API zu stellen.
Konfigurieren des SDK
Das Apollo-Client-SDK muss auf einen GraphQL-Server verweisen, damit es automatisch den Boilerplate-Code verarbeiten kann, der normalerweise für eine solche Integration benötigt wird. Genau das haben wir also getan, als wir src/apollo.js beim Einrichten der Frontend-App geändert haben.
Eine GraphQL-Abonnementanfrage stellen (Live-Abfrage)
Definieren Sie das Abonnement, das wir uns im vorherigen Abschnitt angesehen haben, in der Datei src/GraphQL.jsx :
const SUBSCRIPTION_RESULT = ` subscription getResult($pollId: uuid!) { poll_results ( order_by: option_id_desc, where: { poll_id: {_eq: $pollId} } ) { option_id option { id text } votes } }`;
Wir verwenden diese Definition, um unsere React-Komponente zu verdrahten:
export const Result = (pollId) => ( <Subscription subscription={gql`${SUBSCRIPTION_RESULT}`} variables={pollId}> {({ loading, error, data }) => { if (loading) return
Wird geladen...</p>; wenn (Fehler) zurück
Fehler :</p>; Rückkehr ( <div> <div> {RenderChart(Daten)} </div> </div> ); }} </Abonnement> )
Hier ist zu beachten, dass das obige Abonnement auch eine Abfrage hätte sein können. Das bloße Ersetzen eines Schlüsselworts durch ein anderes gibt uns eine „Live-Abfrage“, und das ist alles, was das Apollo Client SDK braucht, um diese Echtzeit-API mit Ihrer App zu verknüpfen. Jedes Mal, wenn es einen neuen Datensatz aus unserer Live-Abfrage gibt, löst das SDK ein erneutes Rendern unseres Diagramms mit diesen aktualisierten Daten aus (unter Verwendung des renderChart(data)
). Das ist es. Es ist wirklich so einfach!
Abschließende Gedanken
In drei einfachen Schritten (Erstellen eines GraphQL-Backends, Modellieren des App-Schemas und Integrieren des Frontends in die GraphQL-API) können Sie schnell eine voll funktionsfähige Echtzeit-App erstellen, ohne sich mit unnötigen Details wie der Einrichtung zu beschäftigen eine Websocket-Verbindung. Genau hier liegt die Leistungsfähigkeit von Community-Tools, die eine Abstraktion wie GraphQL unterstützen.
Wenn Sie dies interessant fanden und GraphQL für Ihr nächstes Nebenprojekt oder Ihre nächste Produktionsanwendung weiter erforschen möchten, finden Sie hier einige Faktoren, die Sie möglicherweise zum Erstellen Ihrer GraphQL-Toolchain verwenden möchten:
- Leistung und Skalierbarkeit
GraphQL soll direkt von Frontend-Apps verwendet werden (es ist nicht besser als ein ORM im Backend; dadurch ergeben sich echte Produktivitätsvorteile). Ihre Tools müssen also intelligent sein, um Datenbankverbindungen effizient zu nutzen, und sollten sich mühelos skalieren lassen. - Sicherheit
Aus dem Obigen folgt, dass ein ausgereiftes rollenbasiertes Zugriffskontrollsystem erforderlich ist, um den Zugriff auf Daten zu autorisieren. - Automatisierung
Wenn Sie neu im GraphQL-Ökosystem sind, mag das manuelle Schreiben eines GraphQL-Schemas und die Implementierung eines GraphQL-Servers wie eine entmutigende Aufgabe erscheinen. Maximieren Sie die Automatisierung Ihrer Tools, damit Sie sich auf die wichtigen Dinge wie das Erstellen benutzerzentrierter Frontend-Funktionen konzentrieren können. - Die Architektur So trivial die obigen Bemühungen auch erscheinen mögen, die Backend-Architektur einer produktionstauglichen App kann fortschrittliche GraphQL-Konzepte wie Schema-Stitching usw. umfassen. Darüber hinaus eröffnet die Fähigkeit, Echtzeit-APIs einfach zu generieren/verbrauchen, die Möglichkeit, asynchrone, reaktive APIs zu erstellen Apps, die belastbar und von Natur aus skalierbar sind. Daher ist es wichtig zu bewerten, wie GraphQL-Tools Ihre Architektur optimieren können.
Ähnliche Resourcen
- Sie können sich hier eine Live-Version der App ansehen.
- Der vollständige Quellcode ist auf GitHub verfügbar.
- Wenn Sie das Datenbankschema untersuchen und GraphQL-Testabfragen ausführen möchten, können Sie dies hier tun.