Cum să construiți o aplicație în timp real cu abonamente GraphQL pe Postgres
Publicat: 2022-03-10În acest articol, vom arunca o privire asupra provocărilor implicate în construirea de aplicații în timp real și asupra modului în care instrumentele emergente le abordează cu soluții elegante, care sunt ușor de raționat. Pentru a face acest lucru, vom construi o aplicație de sondare în timp real (cum ar fi un sondaj Twitter cu statistici generale în timp real) doar folosind Postgres, GraphQL, React și fără cod backend!
Accentul principal va fi pe configurarea backend-ului (implementarea instrumentelor gata de utilizat, modelarea schemei) și aspectele integrării frontend-ului cu GraphQL și mai puțin asupra UI/UX a front-end-ului (va vor ajuta unele cunoștințe despre ReactJS). Secțiunea de tutorial va adopta o abordare pictată după numere, așa că vom clona doar un depozit GitHub pentru modelarea schemei și interfața de utilizare și o vom modifica, în loc să construim întreaga aplicație de la zero.
Toate lucrurile GraphQL
Știi tot ce trebuie să știi despre GraphQL? Dacă aveți îndoieli, Eric Baer vă oferă un ghid detaliat despre originile sale, dezavantajele sale și elementele de bază despre cum să lucrați cu el. Citiți un articol înrudit →
Înainte de a continua să citiți acest articol, aș dori să menționez că o cunoaștere practică a următoarelor tehnologii (sau înlocuitori) este benefică:
- ReactJS
Acesta poate fi înlocuit cu orice cadru frontend, Android sau IOS, urmând documentația bibliotecii client. - Postgres
Puteți lucra cu alte baze de date, dar cu instrumente diferite, principiile prezentate în această postare se vor aplica în continuare.
De asemenea, puteți adapta acest context tutorial pentru alte aplicații în timp real foarte ușor.

După cum este ilustrat de sarcina utilă GraphQL însoțitoare din partea de jos, există trei caracteristici majore pe care trebuie să le implementăm:
- Preluați întrebarea sondajului și o listă de opțiuni (stânga sus).
- Permiteți unui utilizator să voteze pentru o anumită întrebare din sondaj (butonul „Votați”).
- Preluați rezultatele sondajului în timp real și afișați-le într-un grafic cu bare (dreapta sus; putem trece peste funcția pentru a obține o listă a utilizatorilor actuali online, deoarece este o replică exactă a acestui caz de utilizare).
Provocări legate de construirea de aplicații în timp real
Crearea de aplicații în timp real (mai ales ca dezvoltator de front-end sau cineva care a făcut recent o tranziție pentru a deveni un dezvoltator fullstack) este o problemă de inginerie greu de rezolvat. Acesta este, în general, modul în care funcționează aplicațiile contemporane în timp real (în contextul aplicației noastre exemplu):
- Interfața actualizează o bază de date cu unele informații; Votul unui utilizator este trimis către backend, adică sondaj/opțiune și informații despre utilizator (
user_id
,option_id
). - Prima actualizare declanșează un alt serviciu care agregează datele sondajului pentru a reda o ieșire care este transmisă înapoi la aplicație în timp real (de fiecare dată când este exprimat un nou vot de către cineva; dacă acest lucru se face eficient, doar datele sondajului actualizat sunt procesate și sunt actualizați doar acei clienți care s-au abonat la acest sondaj):
- Datele de vot sunt mai întâi procesate de un serviciu
register_vote
(să presupunem că aici are loc o anumită validare) care declanșează un serviciupoll_results
. - Datele de sondaj agregate în timp real sunt transmise de serviciul
poll_results
către front-end pentru afișarea statisticilor generale.
- Datele de vot sunt mai întâi procesate de un serviciu

Acest model este derivat dintr-o abordare tradițională de construire a API-ului și, în consecință, are probleme similare:
- Oricare dintre pașii succesivi ar putea merge prost, lăsând UX-ul suspendat și afectând alte operațiuni independente.
- Necesită mult efort pe stratul API, deoarece este un singur punct de contact pentru aplicația frontend, care interacționează cu mai multe servicii. De asemenea, trebuie să implementeze un API în timp real bazat pe websockets - nu există un standard universal pentru acest lucru și, prin urmare, vede suport limitat pentru automatizare în instrumente.
- Aplicația frontend trebuie să adauge instalațiile necesare pentru a consuma API-ul în timp real și poate, de asemenea, trebuie să rezolve problema consecvenței datelor observată de obicei în aplicațiile în timp real (mai puțin importantă în exemplul ales, dar critică în comandarea mesajelor într-un mod real). -time aplicație de chat).
- Multe implementări recurg la utilizarea bazelor de date suplimentare non-relaționale pe partea serverului (Firebase, etc.) pentru suport ușor API în timp real.
Să aruncăm o privire la modul în care GraphQL și instrumentele asociate abordează aceste provocări.
Ce este GraphQL?
GraphQL este o specificație pentru un limbaj de interogare pentru API și un timp de execuție pe partea de server pentru executarea interogărilor. Această specificație a fost dezvoltată de Facebook pentru a accelera dezvoltarea aplicațiilor și pentru a oferi un format de acces la date standardizat, independent de bazele de date. Orice server GraphQL compatibil cu specificațiile trebuie să accepte următoarele:
- Interogări pentru citiri
Un tip de solicitare pentru solicitarea de date imbricate de la o sursă de date (care poate fi una sau o combinație a unei baze de date, un API REST sau o altă schemă/server GraphQL). - Mutații pentru scrieri
Un tip de solicitare pentru scrierea/retransmiterea datelor în sursele de date menționate mai sus. - Abonamente pentru interogări live
Un tip de solicitare pentru clienții să se aboneze la actualizări în timp real.
GraphQL folosește și o schemă tipizată. Ecosistemul are o mulțime de instrumente care vă ajută să identificați erorile în timpul dezvoltării/compilării, ceea ce duce la mai puține erori de rulare.
Iată de ce GraphQL este excelent pentru aplicațiile în timp real:
- Interogările live (abonamente) sunt o parte implicită a specificației GraphQL. Orice sistem GraphQL trebuie să aibă capabilități API native în timp real.
- O specificație standard pentru interogări în timp real a consolidat eforturile comunității în jurul instrumentelor la nivelul clientului, rezultând un mod foarte intuitiv de integrare cu API-urile GraphQL.
GraphQL și o combinație de instrumente open-source pentru evenimentele bazei de date și funcțiile serverless/cloud oferă un substrat excelent pentru construirea de aplicații native cloud cu logica de afaceri asincronă și caracteristici în timp real care sunt ușor de construit și gestionat. Această nouă paradigmă are ca rezultat, de asemenea, o experiență excelentă pentru utilizatori și dezvoltatori.
În restul acestui articol, voi folosi instrumente open-source pentru a construi o aplicație bazată pe această diagramă de arhitectură:

Crearea unei aplicații de sondaj/votare în timp real
Cu această introducere în GraphQL, să revenim la construirea aplicației de sondare așa cum este descris în prima secțiune.
Cele trei caracteristici (sau povești evidențiate) au fost alese pentru a demonstra diferitele tipuri de solicitări GraphQL pe care le va face aplicația noastră:
- Interogare
Preluați întrebarea sondajului și opțiunile acesteia. - Mutaţie
Permiteți unui utilizator să voteze. - Abonament
Afișați un tablou de bord în timp real pentru rezultatele sondajului.

Cerințe preliminare
- Un cont Heroku (utilizați nivelul gratuit, nu este necesar un card de credit)
Pentru a implementa un backend GraphQL (vezi următorul punct de mai jos) și o instanță Postgres. - Hasura GraphQL Engine (gratuit, cu sursă deschisă) Un server GraphQL gata de utilizat pe Postgres.
- Apollo Client (SDK gratuit, open-source)
Pentru integrarea cu ușurință a aplicațiilor clienților cu un server GraphQL. - npm (manager de pachete gratuit, open-source)
Pentru a rula aplicația noastră React.
Implementarea bazei de date și a unui backend GraphQL
Vom implementa câte o instanță Postgres și GraphQL Engine pe nivelul gratuit Heroku. Putem folosi un buton Heroku ingenios pentru a face acest lucru cu un singur clic.

Notă: De asemenea, puteți să urmați acest link sau să căutați documentația de implementare Hasura GraphQL pentru Heroku (sau alte platforme).

Nu veți avea nevoie de nicio configurație suplimentară și puteți doar să faceți clic pe butonul „Implementați aplicația”. După finalizarea implementării, notați adresa URL a aplicației:
<app-name>.herokuapp.com
De exemplu, în captura de ecran de mai sus, ar fi:
hge-realtime-app-tutorial.herokuapp.com
Ceea ce am făcut până acum a fost să implementăm o instanță de Postgres (ca un add-on în limbajul Heroku) și o instanță de GraphQL Engine care este configurată să utilizeze această instanță Postgres. Ca urmare a acestui lucru, avem acum un API GraphQL gata de utilizat, dar, din moment ce nu avem tabele sau date în baza noastră de date, acest lucru nu este încă util. Deci, să abordăm acest lucru imediat.
Modelarea schemei bazei de date
Următoarea diagramă de schemă surprinde o schemă simplă a bazei de date relaționale pentru aplicația noastră de sondaj:


După cum puteți vedea, schema este una simplă, normalizată, care folosește constrângerile cheii externe. Aceste constrângeri sunt interpretate de Motorul GraphQL ca relații 1:1 sau 1:multe (de exemplu poll:options
este o relație 1: multe, deoarece fiecare sondaj va avea mai mult de o opțiune care sunt legate de constrângerea cheii externe între coloana id
din tabelul de poll
și coloana poll_id
din tabelul de option
). Datele înrudite pot fi modelate ca un grafic și pot astfel alimenta un API GraphQL. Acesta este exact ceea ce face Motorul GraphQL.
Pe baza celor de mai sus, va trebui să creăm următoarele tabele și constrângeri pentru a modela schema noastră:
-
Poll
Un tabel pentru a capta întrebarea sondajului. -
Option
Opțiuni pentru fiecare sondaj. -
Vote
Pentru a înregistra votul unui utilizator. - Constrângere de cheie externă între următoarele câmpuri (
table : column
):-
option : poll_id → poll : id
-
vote : poll_id → poll : id
-
vote : created_by_user_id → user : id
-
Acum că avem designul schemei noastre, să o implementăm în baza noastră de date Postgres. Pentru a aduce instantaneu această schemă, iată ce vom face:
- Descărcați CLI-ul GraphQL Engine.
- Clonează acest depozit:
$ git clone clone https://github.com/hasura/graphql-engine $ cd graphql-engine/community/examples/realtime-poll
- Accesați
hasura/
și editațiconfig.yaml
:endpoint: https://<app-name>.herokuapp.com
- Aplicați migrațiile folosind CLI, din interiorul directorului proiectului (pe care tocmai l-ați descărcat prin clonare):
$ hasura migrate apply
Asta e pentru backend. Acum puteți deschide consola GraphQL Engine și puteți verifica dacă toate tabelele sunt prezente (consola este disponibilă la https://<app-name>.herokuapp.com/console
).
Notă: Ați putea, de asemenea, să utilizați consola pentru a implementa schema creând tabele individuale și apoi adăugând constrângeri folosind o interfață de utilizare. Utilizarea suportului încorporat pentru migrări în GraphQL Engine este doar o opțiune convenabilă, care a fost disponibilă deoarece exemplul nostru de depozit are migrații pentru a afișa tabelele necesare și pentru a configura relații/constrângeri (acest lucru este, de asemenea, foarte recomandat, indiferent dacă construiți un hobby). proiect sau o aplicație pregătită pentru producție).
Integrarea aplicației Frontend React cu backend-ul GraphQL
Interfața din acest tutorial este o aplicație simplă care afișează întrebarea sondajului, opțiunea de a vota și rezultatele sondajului agregat într-un singur loc. După cum am menționat mai devreme, ne vom concentra mai întâi pe rularea acestei aplicații, astfel încât să obțineți satisfacția imediată de a utiliza API-ul GraphQL recent implementat, vedeți cum conceptele GraphQL pe care le-am analizat mai devreme în acest articol alimentează diferitele cazuri de utilizare ale unei astfel de aplicații , apoi explorați cum funcționează integrarea GraphQL sub capotă.
NOTĂ: Dacă sunteți nou în ReactJS, poate doriți să consultați unele dintre aceste articole. Nu vom intra în detaliile părții React a aplicației și, în schimb, ne vom concentra mai mult pe aspectele GraphQL ale aplicației. Puteți consulta codul sursă din repo pentru orice detalii despre cum a fost creată aplicația React .
Configurarea aplicației Frontend
- În repo-ul clonat în secțiunea anterioară, editați
HASURA_GRAPHQL_ENGINE_HOSTNAME
în fișierul src/apollo.js (în dosarul/community/examples/realtime-poll
) și setați-l la adresa URL a aplicației Heroku de mai sus:export const HASURA_GRAPHQL_ENGINE_HOSTNAME = 'random-string-123.herokuapp.com';
- Accesați rădăcina depozitului/dosarului aplicației (
/realtime-poll/
) și utilizați npm pentru a instala modulele necesare și apoi rulați aplicația:$ npm install $ npm start

Ar trebui să vă puteți juca acum cu aplicația. Continuați și votați de câte ori doriți, veți observa că rezultatele se schimbă în timp real. De fapt, dacă configurați o altă instanță a acestei interfețe de utilizare și o direcționați către același backend, veți putea vedea rezultate agregate în toate instanțele.
Deci, cum folosește această aplicație GraphQL? Citește mai departe.
În culise: GraphQL
În această secțiune, vom explora funcțiile GraphQL care alimentează aplicația, urmate de o demonstrație a ușurinței integrării în următoarea.
Componenta sondajului și graficul rezultatelor agregate
Componenta de sondaj din stânga sus, care preia un sondaj cu toate opțiunile sale și captează votul unui utilizator în baza de date. Ambele operațiuni sunt efectuate folosind API-ul GraphQL. Pentru a prelua detaliile unui sondaj, facem o interogare (rețineți acest lucru din introducerea GraphQL?):
query { poll { id question options { id text } } }
Folosind componenta Mutation din react-apollo
, putem conecta mutația la un formular HTML astfel încât mutația să fie executată folosind variabile optionId
și userId
atunci când formularul este trimis:
mutation vote($optionId: uuid!, $userId: uuid!) { insert_vote(objects: [{option_id: $optionId, created_by_user_id: $userId}]) { returning { id } } }
Pentru a afișa rezultatele sondajului, trebuie să derivăm numărul de voturi pe opțiune din datele din tabelul de vot. Putem crea o vizualizare Postgres și o urmărim folosind Motorul GraphQL pentru a face aceste date derivate disponibile prin 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;
Vizualizarea poll_results
unește datele din tabelele de vote
și de poll
pentru a oferi un număr agregat al numărului de voturi pentru fiecare opțiune.
Folosind GraphQL Subscriptions peste această vizualizare, react-google-charts și componenta de abonament din react-apollo
, putem conecta o diagramă reactivă care se actualizează în timp real atunci când are loc un nou vot de la orice client.
subscription getResult($pollId: uuid!) { poll_results(where: {poll_id: {_eq: $pollId}}) { option { id text } votes } }
Integrare API GraphQL
După cum am menționat mai devreme, am folosit Apollo Client, un SDK open-source pentru a integra o aplicație ReactJS cu backend-ul GraphQL. Clientul Apollo este analog oricărei biblioteci client HTTP, cum ar fi cererile pentru python, modulul http standard pentru JavaScript și așa mai departe. Acesta încapsulează detaliile efectuării unei solicitări HTTP (în acest caz solicitări POST). Utilizează configurația (specificată în src/apollo.js
) pentru a face cereri de interogare/mutație/abonament (specificate în src/GraphQL.jsx cu opțiunea de a folosi variabile care pot fi înlocuite dinamic în codul JavaScript al aplicației REACT) pentru a un punct final GraphQL. De asemenea, folosește schema tipizată din spatele punctului final GraphQL pentru a oferi validarea timpului de compilare/dezvoltare pentru cererile menționate mai sus. Să vedem cât de ușor este pentru o aplicație client să facă o solicitare de interogare live (abonament) către API-ul GraphQL.
Configurarea SDK-ului
SDK-ul Apollo Client trebuie să fie îndreptat către un server GraphQL, astfel încât să poată gestiona automat codul standard necesar de obicei pentru o astfel de integrare. Deci, asta este exact ceea ce am făcut când am modificat src/apollo.js când am configurat aplicația frontend.
Efectuarea unei cereri de abonament GraphQL (interogare live)
Definiți abonamentul pe care l-am analizat în secțiunea anterioară în fișierul 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 } }`;
Vom folosi această definiție pentru a conecta componenta noastră React:
export const Result = (pollId) => ( <Subscription subscription={gql`${SUBSCRIPTION_RESULT}`} variables={pollId}> {({ loading, error, data }) => { if (loading) return
Se încarcă...</p>; dacă (eroare) revine
Eroare:</p>; întoarcere ( <div> <div> {renderChart(date)} </div> </div> ); }} </Abonament> )
Un lucru de remarcat aici este că abonamentul de mai sus ar fi putut fi, de asemenea, o interogare. Simpla înlocuire a unui cuvânt cheie cu altul ne oferă o „interogare live”, și de atât este nevoie pentru SDK-ul Apollo Client pentru a conecta acest API în timp real cu aplicația dvs. De fiecare dată când există un nou set de date din interogarea noastră live, SDK-ul declanșează o redare a diagramei noastre cu aceste date actualizate (folosind renderChart(data)
). Asta e. Este chiar atât de simplu!
Gânduri finale
În trei pași simpli (crearea unui backend GraphQL, modelarea schemei aplicației și integrarea front-end-ului cu API-ul GraphQL), puteți conecta rapid o aplicație complet funcțională în timp real, fără a fi blocat în detalii inutile, cum ar fi configurarea. o conexiune websocket. Exact acolo este puterea instrumentelor comunitare care susțin o abstractizare precum GraphQL.
Dacă ați găsit acest lucru interesant și doriți să explorați GraphQL în continuare pentru următorul proiect secundar sau aplicație de producție, iată câțiva factori pe care ați putea dori să-i utilizați pentru construirea lanțului de instrumente GraphQL:
- Performanță și scalabilitate
GraphQL este menit să fie consumat direct de aplicațiile frontend (nu este mai bun decât un ORM în backend; beneficiile reale de productivitate vin din acest lucru). Prin urmare, instrumentele dvs. trebuie să fie inteligente în ceea ce privește utilizarea eficientă a conexiunilor la baze de date și ar trebui să poată scala fără efort. - Securitate
Rezultă de mai sus că este necesar un sistem matur de control al accesului bazat pe roluri pentru a autoriza accesul la date. - Automatizare
Dacă sunteți nou în ecosistemul GraphQL, scrierea de mână a unei scheme GraphQL și implementarea unui server GraphQL pot părea sarcini descurajante. Maximizați automatizarea din instrumentele dvs., astfel încât să vă puteți concentra asupra lucrurilor importante, cum ar fi construirea de funcții de front-end centrate pe utilizator. - Arhitectură Oricât de banale par eforturile de mai sus, arhitectura backend a unei aplicații de nivel de producție poate implica concepte GraphQL avansate, cum ar fi schema-stitching, etc. aplicații care sunt rezistente și scalabile în mod inerent. Prin urmare, este esențial să evaluați modul în care instrumentele GraphQL vă pot eficientiza arhitectura.
Resurse conexe
- Puteți verifica o versiune live a aplicației aici.
- Codul sursă complet este disponibil pe GitHub.
- Dacă doriți să explorați schema bazei de date și să rulați interogări de testare GraphQL, puteți face acest lucru aici.