Jak zbudować aplikację czasu rzeczywistego z subskrypcjami GraphQL na Postgres
Opublikowany: 2022-03-10W tym artykule przyjrzymy się wyzwaniom związanym z tworzeniem aplikacji czasu rzeczywistego oraz tym, jak nowe narzędzia rozwiązują je za pomocą eleganckich rozwiązań, które są łatwe do zrozumienia. Aby to zrobić, zbudujemy aplikację do ankietowania w czasie rzeczywistym (np. ankietę na Twitterze z ogólnymi statystykami w czasie rzeczywistym), używając tylko Postgres, GraphQL, React i bez kodu zaplecza!
Główny nacisk zostanie położony na konfigurację backendu (wdrożenie gotowych do użycia narzędzi, modelowanie schematu) oraz aspekty integracji frontendu z GraphQL, a mniej na UI/UX frontendu (pomoże pewna wiedza o ReactJS). Sekcja samouczka przyjmie metodę malowania po liczbach, więc po prostu sklonujemy repozytorium GitHub do modelowania schematu i interfejsu użytkownika i dostosujemy je, zamiast budować całą aplikację od zera.
Wszystkie rzeczy GraphQL
Czy wiesz wszystko, co musisz wiedzieć o GraphQL? Jeśli masz wątpliwości, Eric Baer udzieli Ci szczegółowego przewodnika po jego początkach, wadach i podstawach pracy z nim. Przeczytaj powiązany artykuł →
Zanim będziesz kontynuować czytanie tego artykułu, chciałbym wspomnieć, że praktyczna znajomość następujących technologii (lub zamienników) jest korzystna:
- ReactJS
Można to zastąpić dowolnym frameworkiem frontendowym, Androidem lub IOS, postępując zgodnie z dokumentacją biblioteki klienta. - Postgres
Możesz pracować z innymi bazami danych, ale z innymi narzędziami, zasady opisane w tym poście będą nadal obowiązywać.
Możesz także bardzo łatwo dostosować ten kontekst samouczka do innych aplikacji czasu rzeczywistego.
Jak ilustruje dołączony ładunek GraphQL na dole, istnieją trzy główne funkcje, które musimy wdrożyć:
- Pobierz pytanie ankiety i listę opcji (u góry po lewej).
- Zezwól użytkownikowi na głosowanie na zadane pytanie ankiety (przycisk „Głosuj”).
- Pobierz wyniki ankiety w czasie rzeczywistym i wyświetl je na wykresie słupkowym (w prawym górnym rogu; możemy pominąć tę funkcję, aby pobrać listę aktualnie online użytkowników, ponieważ jest to dokładna replika tego przypadku użycia).
Wyzwania związane z tworzeniem aplikacji w czasie rzeczywistym
Tworzenie aplikacji czasu rzeczywistego (zwłaszcza jako programista frontend lub ktoś, kto niedawno przeszedł na programistę typu fullstack), jest trudnym problemem inżynieryjnym do rozwiązania. Generalnie tak działają współczesne aplikacje czasu rzeczywistego (w kontekście naszej przykładowej aplikacji):
- Nakładka aktualizuje bazę danych pewnymi informacjami; Głos użytkownika jest wysyłany do backendu, tj. ankiety/opcji i informacji o użytkowniku (
user_id
,option_id
). - Pierwsza aktualizacja uruchamia inną usługę, która agreguje dane ankiety, aby wygenerować dane wyjściowe, które są przekazywane z powrotem do aplikacji w czasie rzeczywistym (za każdym razem, gdy ktoś odda nowy głos; jeśli zostanie to wykonane skutecznie, przetwarzane są tylko zaktualizowane dane ankiety i aktualizowane są tylko ci klienci, którzy zasubskrybowali tę ankietę):
- Dane głosowania są najpierw przetwarzane przez usługę
register_vote
(załóżmy, że odbywa się tutaj pewna walidacja), która uruchamia usługępoll_results
. - Zagregowane dane ankiety w czasie rzeczywistym są przekazywane przez usługę
poll_results
do interfejsu użytkownika w celu wyświetlenia ogólnych statystyk.
- Dane głosowania są najpierw przetwarzane przez usługę
Model ten wywodzi się z tradycyjnego podejścia do budowania API i w konsekwencji ma podobne problemy:
- Każdy z kolejnych kroków może się nie udać, pozostawiając UX zawieszony i wpływając na inne niezależne operacje.
- Wymaga dużego nakładu pracy w warstwie API, ponieważ jest to pojedynczy punkt kontaktu dla aplikacji frontendowej, która współdziała z wieloma usługami. Musi również wdrożyć interfejs API czasu rzeczywistego oparty na gniazdach sieciowych — nie ma uniwersalnego standardu i dlatego widzi ograniczone wsparcie dla automatyzacji w narzędziach.
- Aplikacja frontendowa jest wymagana, aby dodać niezbędną instalację w celu wykorzystania interfejsu API czasu rzeczywistego, a także może być zmuszona do rozwiązania problemu spójności danych zwykle występującego w aplikacjach czasu rzeczywistego (mniej ważne w naszym wybranym przykładzie, ale krytyczne przy porządkowaniu wiadomości w rzeczywistym -czas aplikacji czatu).
- Wiele implementacji korzysta z dodatkowych nierelacyjnych baz danych po stronie serwera (Firebase itp.) w celu łatwej obsługi interfejsu API w czasie rzeczywistym.
Przyjrzyjmy się, jak GraphQL i związane z nim narzędzia radzą sobie z tymi wyzwaniami.
Co to jest GraphQL?
GraphQL to specyfikacja języka zapytań dla interfejsów API oraz środowiska wykonawczego po stronie serwera do wykonywania zapytań. Ta specyfikacja została opracowana przez Facebooka, aby przyspieszyć tworzenie aplikacji i zapewnić ustandaryzowany, niezależny od bazy danych format dostępu do danych. Każdy zgodny ze specyfikacją serwer GraphQL musi obsługiwać następujące elementy:
- Zapytania o odczyty
Typ żądania dla żądania danych zagnieżdżonych ze źródła danych (które może być jedną lub kombinacją bazy danych, interfejsu API REST lub innego schematu/serwera GraphQL). - Mutacje do zapisu
Typ żądania zapisu/przekazania danych do wyżej wymienionych źródeł danych. - Subskrypcje zapytań na żywo
Typ żądania dla klientów, aby subskrybować aktualizacje w czasie rzeczywistym.
GraphQL wykorzystuje również typowany schemat. Ekosystem zawiera wiele narzędzi, które pomagają identyfikować błędy w czasie tworzenia/kompilacji, co skutkuje mniejszą liczbą błędów w czasie wykonywania.
Oto dlaczego GraphQL doskonale nadaje się do aplikacji działających w czasie rzeczywistym:
- Zapytania na żywo (subskrypcje) są niejawną częścią specyfikacji GraphQL. Każdy system GraphQL musi mieć natywne możliwości API czasu rzeczywistego.
- Standardowa specyfikacja zapytań w czasie rzeczywistym skonsolidowała wysiłki społeczności wokół narzędzi po stronie klienta, co zaowocowało bardzo intuicyjnym sposobem integracji z API GraphQL.
GraphQL i połączenie narzędzi typu open source do obsługi zdarzeń bazy danych oraz funkcji bezserwerowych/chmury stanowią doskonałe podłoże do tworzenia aplikacji natywnych dla chmury z asynchroniczną logiką biznesową i funkcjami czasu rzeczywistego, które są łatwe do zbudowania i zarządzania. Ten nowy paradygmat zapewnia również doskonałe wrażenia użytkowników i programistów.
W dalszej części artykułu wykorzystam narzędzia open-source do zbudowania aplikacji na podstawie tego diagramu architektury:
Tworzenie aplikacji do ankiet/głosowania w czasie rzeczywistym
Po tym wprowadzeniu do GraphQL wróćmy do tworzenia aplikacji sondującej, jak opisano w pierwszej sekcji.
Trzy funkcje (lub wyróżnione historie) zostały wybrane, aby zademonstrować różne typy żądań GraphQL, które będzie generować nasza aplikacja:
- Zapytanie
Pobierz pytanie ankiety i jego opcje. - Mutacja
Pozwól użytkownikowi oddać głos. - Subskrypcja
Wyświetlaj pulpit nawigacyjny w czasie rzeczywistym dla wyników ankiety.
Warunki wstępne
- Konto Heroku (użyj poziomu bezpłatnego, karta kredytowa nie jest wymagana)
Aby wdrożyć backend GraphQL (patrz następny punkt poniżej) i instancję Postgres. - Hasura GraphQL Engine (bezpłatny, open-source) Gotowy do użycia serwer GraphQL na Postgresie.
- Klient Apollo (bezpłatny pakiet SDK o otwartym kodzie źródłowym)
Do łatwej integracji aplikacji klienckich z serwerem GraphQL. - npm (darmowy menedżer pakietów o otwartym kodzie źródłowym)
Aby uruchomić naszą aplikację React.
Wdrażanie bazy danych i zaplecza GraphQL
Wdrożymy instancję Postgres i GraphQL Engine na bezpłatnym poziomie Heroku. Możemy użyć sprytnego przycisku Heroku, aby zrobić to jednym kliknięciem.
Uwaga: Możesz również kliknąć ten link lub poszukać dokumentacji wdrożenia Hasura GraphQL dla Heroku (lub innych platform).
Nie będziesz potrzebować żadnej dodatkowej konfiguracji i możesz po prostu kliknąć przycisk „Wdróż aplikację”. Po zakończeniu wdrażania zanotuj adres URL aplikacji:
<app-name>.herokuapp.com
Na przykład na powyższym zrzucie ekranu byłoby to:
hge-realtime-app-tutorial.herokuapp.com
Do tej pory wdrożyliśmy instancję Postgres (jako dodatek w żargonie Heroku) oraz instancję GraphQL Engine, która jest skonfigurowana do korzystania z tej instancji Postgres. W wyniku tego mamy teraz gotowe do użycia API GraphQL, ale ponieważ nie mamy żadnych tabel ani danych w naszej bazie danych, nie jest to jeszcze przydatne. Więc zajmijmy się tym natychmiast.
Modelowanie schematu bazy danych
Poniższy diagram schematu przedstawia prosty schemat relacyjnej bazy danych dla naszej aplikacji ankietowej:
Jak widać, schemat jest prostym, znormalizowanym schematem, który wykorzystuje ograniczenia klucza obcego. To właśnie te ograniczenia są interpretowane przez silnik GraphQL jako relacje 1:1 lub 1:wiele (np poll:options
jest relacją 1:wiele, ponieważ każda sonda będzie miała więcej niż 1 opcję, które są połączone ograniczeniem klucza obcego między kolumna id
tabeli poll
i kolumna poll_id
w tabeli option
). Powiązane dane mogą być modelowane jako wykres, dzięki czemu mogą zasilać GraphQL API. To jest dokładnie to, co robi GraphQL Engine.
W oparciu o powyższe będziemy musieli utworzyć następujące tabele i ograniczenia, aby zamodelować nasz schemat:
-
Poll
Tabela do uchwycenia pytania ankiety. -
Option
Opcje dla każdej ankiety. -
Vote
Aby nagrać głos użytkownika. - Ograniczenie klucza obcego między następującymi polami (
table : column
):-
option : poll_id → poll : id
-
vote : poll_id → poll : id
-
vote : created_by_user_id → user : id
-
Teraz, gdy mamy już projekt schematu, zaimplementujmy go w naszej bazie danych Postgres. Aby natychmiast wyświetlić ten schemat, zrobimy tak:
- Pobierz interfejs wiersza polecenia silnika GraphQL.
- Sklonuj to repozytorium:
$ git clone clone https://github.com/hasura/graphql-engine $ cd graphql-engine/community/examples/realtime-poll
- Przejdź do
hasura/
i edytujconfig.yaml
:endpoint: https://<app-name>.herokuapp.com
- Zastosuj migracje za pomocą CLI, z wnętrza katalogu projektu (który właśnie pobrałeś przez klonowanie):
$ hasura migrate apply
To tyle, jeśli chodzi o zaplecze. Możesz teraz otworzyć konsolę GraphQL Engine i sprawdzić, czy wszystkie tabele są obecne (konsola jest dostępna pod adresem https://<app-name>.herokuapp.com/console
).
Uwaga: Do zaimplementowania schematu można było również użyć konsoli, tworząc poszczególne tabele, a następnie dodając ograniczenia za pomocą interfejsu użytkownika. Korzystanie z wbudowanej obsługi migracji w silniku GraphQL jest po prostu wygodną opcją, która była dostępna, ponieważ nasze przykładowe repozytorium zawiera migracje do wyświetlania wymaganych tabel i konfigurowania relacji/ograniczeń (jest to również wysoce zalecane niezależnie od tego, czy budujesz hobby projektu lub aplikacji gotowej do produkcji).
Integracja aplikacji Frontend React z zapleczem GraphQL
Interfejs w tym samouczku to prosta aplikacja, która pokazuje pytanie ankiety, opcję głosowania i zagregowane wyniki ankiety w jednym miejscu. Jak wspomniałem wcześniej, najpierw skupimy się na uruchomieniu tej aplikacji, aby uzyskać natychmiastową satysfakcję z korzystania z naszego niedawno wdrożonego API GraphQL. Zobacz, jak koncepcje GraphQL, które przyjrzeliśmy się wcześniej w tym artykule, zasilają różne przypadki użycia takiej aplikacji , a następnie zbadaj, jak integracja GraphQL działa pod maską.
UWAGA: Jeśli jesteś nowy w ReactJS, możesz zapoznać się z niektórymi z tych artykułów. Nie będziemy wchodzić w szczegóły części React aplikacji, a zamiast tego skupimy się bardziej na aspektach GraphQL aplikacji. Możesz zapoznać się z kodem źródłowym w repozytorium, aby uzyskać szczegółowe informacje o tym, jak zbudowano aplikację React .
Konfiguracja aplikacji frontendowej
- W repozytorium sklonowanym w poprzedniej sekcji, edytuj
HASURA_GRAPHQL_ENGINE_HOSTNAME
w pliku src/apollo.js (wewnątrz folderu/community/examples/realtime-poll
) i ustaw go na adres URL aplikacji Heroku z góry:export const HASURA_GRAPHQL_ENGINE_HOSTNAME = 'random-string-123.herokuapp.com';
- Przejdź do katalogu głównego repozytorium/folderu aplikacji (
/realtime-poll/
) i użyj npm, aby zainstalować wymagane moduły, a następnie uruchom aplikację:$ npm install $ npm start
Powinieneś być teraz w stanie pobawić się aplikacją. Śmiało i głosuj tyle razy, ile chcesz, zauważysz, że wyniki zmieniają się w czasie rzeczywistym. W rzeczywistości, jeśli skonfigurujesz inną instancję tego interfejsu użytkownika i skierujesz ją na ten sam backend, będziesz mógł zobaczyć wyniki zagregowane we wszystkich instancjach.
Jak więc ta aplikacja korzysta z GraphQL? Czytaj.
Za kulisami: GraphQL
W tej sekcji omówimy funkcje GraphQL napędzające aplikację, a następnie zademonstrujemy łatwość integracji w następnej.
Komponent ankiety i wykres wyników zagregowanych
Komponent ankiety w lewym górnym rogu, który pobiera ankietę ze wszystkimi jej opcjami i przechwytuje głos użytkownika w bazie danych. Obie te operacje są wykonywane przy użyciu GraphQL API. Aby pobrać szczegóły ankiety, wykonujemy zapytanie (pamiętasz to ze wstępu do GraphQL?):
query { poll { id question options { id text } } }
Używając komponentu Mutation z react-apollo
, możemy połączyć mutację z formularzem HTML w taki sposób, że mutacja zostanie wykonana przy użyciu zmiennych optionId
i userId
po przesłaniu formularza:
mutation vote($optionId: uuid!, $userId: uuid!) { insert_vote(objects: [{option_id: $optionId, created_by_user_id: $userId}]) { returning { id } } }
Aby pokazać wyniki ankiety, musimy wyliczyć liczbę głosów na opcję z danych w tabeli głosów. Możemy utworzyć widok Postgres i śledzić go za pomocą GraphQL Engine, aby udostępnić te dane pochodne przez 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;
Widok poll_results
łączy dane z tabel voice i poll
, aby zapewnić łączną liczbę vote
na każdą opcję.
Korzystając z subskrypcji react-apollo
Apollo , możemy utworzyć wykres reaktywny, który aktualizuje się w czasie rzeczywistym, gdy nastąpi nowy głos od dowolnego klienta.
subscription getResult($pollId: uuid!) { poll_results(where: {poll_id: {_eq: $pollId}}) { option { id text } votes } }
Integracja API GraphQL
Jak wspomniałem wcześniej, do integracji aplikacji ReactJS z backendem GraphQL użyłem programu Apollo Client, SDK o otwartym kodzie źródłowym. Klient Apollo jest analogiczny do dowolnej biblioteki klienta HTTP, takiej jak żądania dla Pythona, standardowy moduł http dla JavaScript i tak dalej. Zawiera szczegóły wykonania żądania HTTP (w tym przypadku żądania POST). Używa konfiguracji (określonej w src/apollo.js
) do tworzenia żądań zapytań/mutacji/subskrypcji (określonych w src/GraphQL.jsx z opcją użycia zmiennych, które mogą być dynamicznie podstawiane w kodzie JavaScript aplikacji REACT) do punkt końcowy GraphQL. Wykorzystuje również typowany schemat za punktem końcowym GraphQL, aby zapewnić weryfikację czasu kompilacji/dev dla wyżej wymienionych żądań. Zobaczmy, jak łatwo aplikacja kliencka może wykonać zapytanie na żywo (subskrypcję) do interfejsu API GraphQL.
Konfiguracja SDK
Pakiet Apollo Client SDK musi być skierowany na serwer GraphQL, aby mógł automatycznie obsłużyć kod wzorcowy zwykle potrzebny do takiej integracji. A więc dokładnie to zrobiliśmy, kiedy zmodyfikowaliśmy src/apollo.js podczas konfigurowania aplikacji frontendowej.
Składanie wniosku o subskrypcję GraphQL (zapytanie na żywo)
Zdefiniuj subskrypcję, którą omówiliśmy w poprzedniej sekcji w pliku 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 } }`;
Użyjemy tej definicji, aby połączyć nasz komponent React:
export const Result = (pollId) => ( <Subscription subscription={gql`${SUBSCRIPTION_RESULT}`} variables={pollId}> {({ loading, error, data }) => { if (loading) return
Ładowanie...</p>; jeśli (błąd) zwróć
Błąd :</p>; powrót ( <div> <div> {renderujWykres(dane)} </div> </div> ); }} </Subskrypcja> )
Należy zauważyć, że powyższa subskrypcja również mogła być zapytaniem. Samo zastąpienie jednego słowa kluczowego innym daje nam „zapytanie na żywo” i to wszystko, czego potrzeba, aby pakiet Apollo Client SDK połączył ten interfejs API czasu rzeczywistego z Twoją aplikacją. Za każdym razem, gdy pojawia się nowy zestaw danych z naszego zapytania na żywo, pakiet SDK wyzwala ponowne renderowanie naszego wykresu ze zaktualizowanymi danymi (za pomocą renderChart(data)
). Otóż to. To naprawdę takie proste!
Końcowe przemyślenia
W trzech prostych krokach (tworzenie backendu GraphQL, modelowanie schematu aplikacji i integracja frontendu z API GraphQL) możesz szybko podłączyć w pełni funkcjonalną aplikację czasu rzeczywistego, bez gubienia się w niepotrzebnych szczegółach, takich jak konfiguracja połączenie z gniazdem sieciowym. Właśnie tam jest moc narzędzi społecznościowych wspierających abstrakcję, taką jak GraphQL.
Jeśli uznałeś to za interesujące i chcesz dokładniej zbadać GraphQL dla swojego następnego projektu pobocznego lub aplikacji produkcyjnej, oto kilka czynników, które możesz chcieć wykorzystać do zbudowania swojego łańcucha narzędzi GraphQL:
- Wydajność i skalowalność
GraphQL ma być używany bezpośrednio przez aplikacje frontendowe (nie jest to lepsze niż ORM w backendzie; z tego wynikają realne korzyści produktywności). Dlatego Twoje oprzyrządowanie musi być sprytne, jeśli chodzi o efektywne korzystanie z połączeń z bazą danych i powinno być w stanie bez wysiłku skalować. - Bezpieczeństwo
Z powyższego wynika, że do autoryzacji dostępu do danych potrzebny jest dojrzały, oparty na rolach system kontroli dostępu. - Automatyzacja
Jeśli jesteś nowy w ekosystemie GraphQL, ręczne pisanie schematu GraphQL i implementacja serwera GraphQL może wydawać się trudnymi zadaniami. Zmaksymalizuj automatyzację swoich narzędzi, aby skupić się na ważnych rzeczach, takich jak tworzenie funkcji frontendu zorientowanych na użytkownika. - Architektura Choć powyższe wysiłki wydają się trywialne, architektura zaplecza aplikacji klasy produkcyjnej może obejmować zaawansowane koncepcje GraphQL, takie jak łączenie schematów itp. Co więcej, możliwość łatwego generowania/używania interfejsów API w czasie rzeczywistym otwiera możliwość budowania asynchronicznych, reaktywnych aplikacje, które są odporne i z natury skalowalne. Dlatego bardzo ważne jest, aby ocenić, w jaki sposób narzędzia GraphQL mogą usprawnić Twoją architekturę.
Powiązane zasoby
- Aktualną wersję aplikacji możesz sprawdzić tutaj.
- Pełny kod źródłowy jest dostępny na GitHub.
- Jeśli chcesz poznać schemat bazy danych i uruchomić testowe zapytania GraphQL, możesz to zrobić tutaj.