Comment créer une application en temps réel avec des abonnements GraphQL sur Postgres
Publié: 2022-03-10Dans cet article, nous examinerons les défis liés à la création d'applications en temps réel et la manière dont les outils émergents les relèvent avec des solutions élégantes et faciles à raisonner. Pour ce faire, nous allons créer une application de sondage en temps réel (comme un sondage Twitter avec des statistiques globales en temps réel) simplement en utilisant Postgres, GraphQL, React et aucun code backend !
L'accent principal sera mis sur la configuration du backend (déploiement des outils prêts à l'emploi, modélisation de schéma), et les aspects de l'intégration frontend avec GraphQL et moins sur l'UI/UX du frontend (une certaine connaissance de ReactJS aidera). La section du didacticiel adoptera une approche de peinture par numéros, nous allons donc simplement cloner un référentiel GitHub pour la modélisation du schéma et l'interface utilisateur et le modifier, au lieu de créer l'intégralité de l'application à partir de zéro.
Tout ce qui concerne GraphQL
Savez-vous tout ce que vous devez savoir sur GraphQL ? Si vous avez des doutes, Eric Baer vous propose un guide détaillé sur ses origines, ses inconvénients et les bases de son utilisation. Lire un article connexe →
Avant de continuer à lire cet article, je voudrais mentionner qu'une connaissance pratique des technologies (ou substituts) suivantes est bénéfique :
- ReactJS
Cela peut être remplacé par n'importe quel framework frontal, Android ou IOS en suivant la documentation de la bibliothèque cliente. - postgres
Vous pouvez travailler avec d'autres bases de données mais avec des outils différents, les principes décrits dans cet article s'appliqueront toujours.
Vous pouvez également adapter très facilement ce contexte de didacticiel à d'autres applications en temps réel.
Comme illustré par la charge utile GraphQL ci-jointe en bas, il y a trois fonctionnalités principales que nous devons implémenter :
- Récupérez la question du sondage et une liste d'options (en haut à gauche).
- Permettre à un utilisateur de voter pour une question de sondage donnée (le bouton "Voter").
- Récupérez les résultats du sondage en temps réel et affichez-les dans un graphique à barres (en haut à droite ; nous pouvons passer sous silence la fonctionnalité pour récupérer une liste des utilisateurs actuellement en ligne car il s'agit d'une réplique exacte de ce cas d'utilisation).
Défis liés à la création d'applications en temps réel
La création d'applications en temps réel (en particulier en tant que développeur frontend ou quelqu'un qui a récemment fait la transition pour devenir un développeur fullstack) est un problème d'ingénierie difficile à résoudre. C'est généralement ainsi que fonctionnent les applications en temps réel contemporaines (dans le contexte de notre exemple d'application) :
- L'interface met à jour une base de données avec certaines informations ; Le vote d'un utilisateur est envoyé au backend, c'est-à-dire sondage/option et informations sur l'utilisateur (
user_id
,option_id
). - La première mise à jour déclenche un autre service qui agrège les données du sondage pour rendre une sortie qui est relayée à l'application en temps réel (chaque fois qu'un nouveau vote est exprimé par quelqu'un ; si cela est fait efficacement, seules les données du sondage mises à jour sont traitées et seuls les clients qui se sont abonnés à ce sondage sont mis à jour) :
- Les données de vote sont d'abord traitées par un service
register_vote
(en supposant qu'une validation se produise ici) qui déclenche un servicepoll_results
. - Les données de sondage agrégées en temps réel sont relayées par le service
poll_results
à l'interface pour afficher des statistiques globales.
- Les données de vote sont d'abord traitées par un service
Ce modèle est dérivé d'une approche traditionnelle de création d'API et présente par conséquent des problèmes similaires :
- N'importe laquelle des étapes séquentielles pourrait mal tourner, laissant l'UX suspendue et affectant d'autres opérations indépendantes.
- Nécessite beaucoup d'efforts sur la couche API car il s'agit d'un point de contact unique pour l'application frontale, qui interagit avec plusieurs services. Il doit également implémenter une API en temps réel basée sur des websockets - il n'y a pas de norme universelle pour cela et voit donc un support limité pour l'automatisation dans les outils.
- L'application frontale doit ajouter la plomberie nécessaire pour consommer l'API en temps réel et peut également avoir à résoudre le problème de cohérence des données généralement rencontré dans les applications en temps réel (moins important dans l'exemple que nous avons choisi, mais essentiel pour commander les messages dans un vrai -application de chat de temps).
- De nombreuses implémentations recourent à l'utilisation de bases de données non relationnelles supplémentaires côté serveur (Firebase, etc.) pour une prise en charge facile de l'API en temps réel.
Voyons comment GraphQL et les outils associés relèvent ces défis.
Qu'est-ce que GraphQL ?
GraphQL est une spécification pour un langage de requête pour les API et un environnement d'exécution côté serveur pour l'exécution de requêtes. Cette spécification a été développée par Facebook pour accélérer le développement d'applications et fournir un format d'accès aux données standardisé et indépendant de la base de données. Tout serveur GraphQL conforme aux spécifications doit prendre en charge les éléments suivants :
- Requêtes pour les lectures
Un type de demande pour demander des données imbriquées à partir d'une source de données (qui peut être une base de données ou une combinaison d'une base de données, d'une API REST ou d'un autre schéma/serveur GraphQL). - Mutations pour les écritures
Un type de requête pour écrire/retransmettre des données dans les sources de données susmentionnées. - Abonnements pour les requêtes en direct
Un type de demande permettant aux clients de s'abonner aux mises à jour en temps réel.
GraphQL utilise également un schéma typé. L'écosystème dispose de nombreux outils qui vous aident à identifier les erreurs au moment du développement/de la compilation, ce qui réduit le nombre de bogues d'exécution.
Voici pourquoi GraphQL est idéal pour les applications en temps réel :
- Les requêtes en direct (abonnements) font implicitement partie de la spécification GraphQL. Tout système GraphQL doit avoir des capacités d'API natives en temps réel.
- Une spécification standard pour les requêtes en temps réel a consolidé les efforts de la communauté autour des outils côté client, résultant en une manière très intuitive d'intégrer les API GraphQL.
GraphQL et une combinaison d'outils open source pour les événements de base de données et les fonctions sans serveur/cloud offrent un excellent substrat pour créer des applications cloud natives avec une logique métier asynchrone et des fonctionnalités en temps réel faciles à créer et à gérer. Ce nouveau paradigme se traduit également par une excellente expérience utilisateur et développeur.
Dans la suite de cet article, j'utiliserai des outils open source pour créer une application basée sur ce schéma d'architecture :
Créer une application de sondage/vote en temps réel
Avec cette introduction à GraphQL, revenons à la création de l'application de sondage comme décrit dans la première section.
Les trois fonctionnalités (ou histoires mises en évidence) ont été choisies pour démontrer les différents types de requêtes GraphQL que notre application fera :
- Mettre en doute
Récupérez la question du sondage et ses options. - Mutation
Laissez un utilisateur voter. - Abonnement
Affichez un tableau de bord en temps réel pour les résultats des sondages.
Conditions préalables
- Un compte Heroku (utilisez le niveau gratuit, aucune carte de crédit requise)
Pour déployer un backend GraphQL (voir point suivant ci-dessous) et une instance Postgres. - Moteur Hasura GraphQL (gratuit, open-source) Un serveur GraphQL prêt à l'emploi sur Postgres.
- Client Apollo (SDK gratuit et open source)
Pour intégrer facilement des applications clientes avec un serveur GraphQL. - npm (gestionnaire de paquets gratuit et open source)
Pour exécuter notre application React.
Déployer la base de données et un backend GraphQL
Nous déploierons une instance de Postgres et GraphQL Engine sur le niveau gratuit de Heroku. Nous pouvons utiliser un astucieux bouton Heroku pour le faire en un seul clic.
Remarque : Vous pouvez également suivre ce lien ou rechercher la documentation Déploiement Hasura GraphQL pour Heroku (ou d'autres plates-formes).
Vous n'aurez besoin d'aucune configuration supplémentaire, et vous pouvez simplement cliquer sur le bouton "Déployer l'application". Une fois le déploiement terminé, notez l'URL de l'application :
<app-name>.herokuapp.com
Par exemple, dans la capture d'écran ci-dessus, ce serait :
hge-realtime-app-tutorial.herokuapp.com
Jusqu'à présent, nous avons déployé une instance de Postgres (en tant que module complémentaire dans le langage Heroku) et une instance de GraphQL Engine configurée pour utiliser cette instance de Postgres. En conséquence, nous avons maintenant une API GraphQL prête à l'emploi mais, comme nous n'avons pas de tables ou de données dans notre base de données, cela n'est pas encore utile. Alors, abordons cela immédiatement.
Modélisation du schéma de la base de données
Le diagramme de schéma suivant capture un schéma de base de données relationnelle simple pour notre application de sondage :
Comme vous pouvez le voir, le schéma est un schéma simple et normalisé qui exploite les contraintes de clé étrangère. Ce sont ces contraintes qui sont interprétées par le moteur GraphQL comme des relations 1: 1 ou 1: plusieurs (par exemple poll:options
est une relation 1: plusieurs puisque chaque sondage aura plus d'une option liée par la contrainte de clé étrangère entre le id
de la table poll
et la colonne poll_id
dans la table des option
). Les données associées peuvent être modélisées sous forme de graphique et peuvent ainsi alimenter une API GraphQL. C'est précisément ce que fait le moteur GraphQL.
Sur la base de ce qui précède, nous devrons créer les tables et contraintes suivantes pour modéliser notre schéma :
-
Poll
Un tableau pour saisir la question du sondage. -
Option
Options pour chaque sondage. -
Vote
Pour enregistrer le vote d'un utilisateur. - Contrainte de clé étrangère entre les champs suivants (
table : column
) :-
option : poll_id → poll : id
-
vote : poll_id → poll : id
-
vote : created_by_user_id → user : id
-
Maintenant que nous avons notre conception de schéma, implémentons-la dans notre base de données Postgres. Pour afficher instantanément ce schéma, voici ce que nous allons faire :
- Téléchargez la CLI du moteur GraphQL.
- Clonez ce dépôt :
$ git clone clone https://github.com/hasura/graphql-engine $ cd graphql-engine/community/examples/realtime-poll
- Allez dans
hasura/
et modifiezconfig.yaml
:endpoint: https://<app-name>.herokuapp.com
- Appliquez les migrations à l'aide de la CLI, depuis le répertoire du projet (que vous venez de télécharger par clonage) :
$ hasura migrate apply
C'est tout pour le backend. Vous pouvez maintenant ouvrir la console GraphQL Engine et vérifier que toutes les tables sont présentes (la console est disponible sur https://<app-name>.herokuapp.com/console
).
Remarque : Vous auriez également pu utiliser la console pour implémenter le schéma en créant des tables individuelles, puis en ajoutant des contraintes à l'aide d'une interface utilisateur. L'utilisation de la prise en charge intégrée des migrations dans GraphQL Engine n'est qu'une option pratique qui était disponible car notre exemple de dépôt a des migrations pour afficher les tables requises et configurer les relations/contraintes (ceci est également fortement recommandé, que vous construisiez un passe-temps projet ou une application prête pour la production).
Intégration de l'application Frontend React avec le backend GraphQL
L'interface de ce didacticiel est une application simple qui affiche la question du sondage, la possibilité de voter et les résultats agrégés du sondage en un seul endroit. Comme je l'ai mentionné plus tôt, nous allons d'abord nous concentrer sur l'exécution de cette application afin que vous obteniez la gratification instantanée d'utiliser notre API GraphQL récemment déployée, voyez comment les concepts GraphQL que nous avons examinés plus tôt dans cet article alimentent les différents cas d'utilisation d'une telle application , puis explorez comment l'intégration GraphQL fonctionne sous le capot.
REMARQUE : Si vous débutez avec ReactJS, vous voudrez peut-être consulter certains de ces articles. Nous n'entrerons pas dans les détails de la partie React de l'application, et nous nous concentrerons plutôt sur les aspects GraphQL de l'application. Vous pouvez vous référer au code source dans le dépôt pour plus de détails sur la façon dont l'application React a été construite .
Configuration de l'application frontale
- Dans le référentiel cloné dans la section précédente, modifiez
HASURA_GRAPHQL_ENGINE_HOSTNAME
dans le fichier src/apollo.js (dans le dossier/community/examples/realtime-poll
) et définissez-le sur l'URL de l'application Heroku ci-dessus :export const HASURA_GRAPHQL_ENGINE_HOSTNAME = 'random-string-123.herokuapp.com';
- Accédez à la racine du référentiel/dossier de l'application (
/realtime-poll/
) et utilisez npm pour installer les modules prérequis, puis exécutez l'application :$ npm install $ npm start
Vous devriez pouvoir jouer avec l'application maintenant. Allez-y et votez autant de fois que vous le souhaitez, vous remarquerez que les résultats changent en temps réel. En fait, si vous configurez une autre instance de cette interface utilisateur et que vous la dirigez vers le même backend, vous pourrez voir les résultats agrégés sur toutes les instances.
Alors, comment cette application utilise-t-elle GraphQL ? Continuer à lire.
Dans les coulisses : GraphQL
Dans cette section, nous explorerons les fonctionnalités de GraphQL qui alimentent l'application, suivies d'une démonstration de la facilité d'intégration dans la suivante.
Le composant Sondage et le graphique des résultats agrégés
Le composant de sondage en haut à gauche qui récupère un sondage avec toutes ses options et capture le vote d'un utilisateur dans la base de données. Ces deux opérations sont effectuées à l'aide de l'API GraphQL. Pour récupérer les détails d'un sondage, nous faisons une requête (vous vous en souvenez de l'introduction de GraphQL ?) :
query { poll { id question options { id text } } }
En utilisant le composant Mutation de react-apollo
, nous pouvons connecter la mutation à un formulaire HTML de sorte que la mutation soit exécutée à l'aide des variables optionId
et userId
lorsque le formulaire est soumis :
mutation vote($optionId: uuid!, $userId: uuid!) { insert_vote(objects: [{option_id: $optionId, created_by_user_id: $userId}]) { returning { id } } }
Pour afficher les résultats du sondage, nous devons dériver le nombre de votes par option à partir des données du tableau des votes. Nous pouvons créer une vue Postgres et la suivre à l'aide du moteur GraphQL pour rendre ces données dérivées disponibles sur GraphQL.
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;
La vue poll_results
joint les données des tables de vote
et de poll
pour fournir un décompte agrégé du nombre de votes pour chaque option.
En utilisant les abonnements GraphQL sur cette vue, react-google-charts et le composant d'abonnement de react-apollo
, nous pouvons connecter un graphique réactif qui se met à jour en temps réel lorsqu'un nouveau vote se produit de n'importe quel client.
subscription getResult($pollId: uuid!) { poll_results(where: {poll_id: {_eq: $pollId}}) { option { id text } votes } }
Intégration de l'API GraphQL
Comme je l'ai mentionné précédemment, j'ai utilisé Apollo Client, un SDK open source pour intégrer une application ReactJS au backend GraphQL. Apollo Client est analogue à n'importe quelle bibliothèque client HTTP comme les requêtes pour python, le module http standard pour JavaScript, etc. Il encapsule les détails d'une requête HTTP (dans ce cas, les requêtes POST). Il utilise la configuration (spécifiée dans src/apollo.js
) pour effectuer des requêtes de requête/mutation/abonnement (spécifiée dans src/GraphQL.jsx avec la possibilité d'utiliser des variables qui peuvent être remplacées dynamiquement dans le code JavaScript de votre application REACT) pour un point de terminaison GraphQL. Il exploite également le schéma typé derrière le point de terminaison GraphQL pour fournir une validation du temps de compilation/développement pour les requêtes susmentionnées. Voyons à quel point il est facile pour une application cliente de faire une demande de requête en direct (abonnement) à l'API GraphQL.
Configuration du SDK
Le SDK du client Apollo doit pointer vers un serveur GraphQL, afin qu'il puisse gérer automatiquement le code passe-partout généralement nécessaire pour une telle intégration. Donc, c'est exactement ce que nous avons fait lorsque nous avons modifié src/apollo.js lors de la configuration de l'application frontale.
Faire une demande d'abonnement à GraphQL (Live-Query)
Définissez l'abonnement que nous avons examiné dans la section précédente dans le fichier 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 } }`;
Nous utiliserons cette définition pour câbler notre composant React :
export const Result = (pollId) => ( <Subscription subscription={gql`${SUBSCRIPTION_RESULT}`} variables={pollId}> {({ loading, error, data }) => { if (loading) return
Chargement...</p> ; si (erreur) retour
Erreur :</p> ; retourner ( <div> <div> {renderChart(données)} </div> </div> ); }} </Abonnement> )
Une chose à noter ici est que l'abonnement ci-dessus aurait également pu être une requête. Le simple fait de remplacer un mot-clé par un autre nous donne une "requête en direct", et c'est tout ce qu'il faut au SDK Apollo Client pour connecter cette API en temps réel à votre application. Chaque fois qu'il y a un nouvel ensemble de données de notre requête en direct, le SDK déclenche un nouveau rendu de notre graphique avec ces données mises à jour (à l'aide de l' renderChart(data)
). C'est ça. C'est aussi simple que ça!
Dernières pensées
En trois étapes simples (création d'un backend GraphQL, modélisation du schéma de l'application et intégration du frontend avec l'API GraphQL), vous pouvez rapidement connecter une application en temps réel entièrement fonctionnelle, sans vous perdre dans des détails inutiles tels que la configuration une connexion websocket. C'est là que se trouve la puissance des outils communautaires soutenant une abstraction comme GraphQL.
Si vous avez trouvé cela intéressant et que vous souhaitez explorer davantage GraphQL pour votre prochain projet parallèle ou votre prochaine application de production, voici quelques facteurs que vous voudrez peut-être utiliser pour créer votre chaîne d'outils GraphQL :
- Performances et évolutivité
GraphQL est destiné à être consommé directement par les applications frontales (ce n'est pas mieux qu'un ORM dans le backend ; de réels avantages de productivité en découlent). Vos outils doivent donc être intelligents pour utiliser efficacement les connexions de base de données et doivent pouvoir évoluer sans effort. - Sécurité
Il découle de ce qui précède qu'un système mature de contrôle d'accès basé sur les rôles est nécessaire pour autoriser l'accès aux données. - Automatisation
Si vous êtes nouveau dans l'écosystème GraphQL, l'écriture manuscrite d'un schéma GraphQL et la mise en œuvre d'un serveur GraphQL peuvent sembler des tâches intimidantes. Optimisez l'automatisation de vos outils afin de pouvoir vous concentrer sur les éléments importants tels que la création de fonctionnalités frontales centrées sur l'utilisateur. - Architecture Aussi insignifiants que puissent paraître les efforts ci-dessus, l'architecture backend d'une application de production peut impliquer des concepts GraphQL avancés tels que l'assemblage de schémas, etc. De plus, la possibilité de générer/consommer facilement des API en temps réel ouvre la possibilité de créer des des applications résilientes et intrinsèquement évolutives. Par conséquent, il est essentiel d'évaluer comment les outils GraphQL peuvent rationaliser votre architecture.
Ressources associées
- Vous pouvez consulter une version en direct de l'application ici.
- Le code source complet est disponible sur GitHub.
- Si vous souhaitez explorer le schéma de la base de données et exécuter des requêtes de test GraphQL, vous pouvez le faire ici.