Cześć wszystkim! Maria Ageeva jest programistką Android i od dwóch lat pracuje z technologią GraphQL. W swoim artykule dzieli się doświadczeniami związanymi z użyciem GraphQL, kierując go zarówno do osób, które nigdy wcześniej nie miały z nim do czynienia, jak i do tych, którzy dopiero zaczynają swoją przygodę lub planują wdrożyć GraphQL w swoich projektach. Tekst zawiera również krótki opis pracy z GraphQL na platformie Android.
Co to GraphQL
Najpierw przyjrzyjmy się, czym jest GraphQL. Z definicji z oficjalnej strony internetowej wynika, iż GraphQL to język służący do obsługi zapytań oraz manipulacji danymi dla interfejsu API, a także platforma do wykonywania tych zapytań. Język został w stworzony w 2012 roku przez programistów portalu społecznościowego Facebook i miał służyć wewnętrznym potrzebom firmy. W 2015 roku został udostępniony publicznie, a od 7 listopada 2018 jest rozwijany nie przez Facebooka, a przez GraphQL Foundation. Oczywiście projekt rozwija się dość aktywnie od 2012 roku, ale szczególną popularność zyskał po uzyskaniu statusu open source.
Nawet jeżeli nigdy wcześniej nie słyszałeś o GraphQL, istnieje duża szansa, iż znasz lub korzystałeś z produktów napisanych przy jego użyciu. Po pierwsze, jest to oczywiście portal społecznościowy Facebook. Oprócz tego GraphQL jest wykorzystywany w rozwoju produktów takich jak Airbnb, GitHub, Pinterest, Shopify, New York Times i wielu innych.
Wróćmy do tworzenia języka. Ten fakt, iż został on stworzony specjalnie przez Facebooka do celów projektu, zawierającego dużą ilość heterogenicznych danych, świadczy o tym, iż podczas tworzenia takiego produktu mogą powstawać sytuacje, kiedy architektura REST jest ograniczona. Na przykład założenie profilu użytkownika, dodawanie postów i komentarzy początkowo nie wydaje się zbyt skomplikowanym zadaniem. Ale jeżeli weźmiesz pod uwagę ilość danych w systemie i założysz, iż wszystkie te dane są przechowywane w różnych bazach danych (na przykład MySQL i MongoDB), wtedy zdasz sobie sprawę, iż będzie to wymagało stworzenia kilku REST-endpointów. Ale jeżeli wyobrazisz sobie, jak duży jest wolumen danych i jak heterogeniczne są źródła danych, staje się jasne, dlaczego konieczne było wypracowanie nowego podejścia do pracy z API. U podstaw tego podejścia leży poniższa zasada: lepiej mieć do dyspozycji jeden „inteligentny” endpoint, który będzie w stanie obsługiwać złożone zapytania i zwracać dane dokładnie w takiej formie i zakresie, jakiego potrzebuje klient.
Podstawą każdej implementacji GraphQL API jest schemat danych – jest to opis tego, z jakimi typami danych może pracować i jakie typy danych może zwrócić w odpowiedzi na zapytanie (są one opisane w systemie typów GraphQL). Mówiąc najprościej, aby pracować z dowolnym API, użytkownik musi wiedzieć, jakie typy obiektów można uzyskać, jakie pola wybrać, jakie pola są dostępne w obiektach wewnętrznych itp. I tu się przyda nam schemat.
Jak widać, podczas pracy z GraphQL API klienta w ogóle nie obchodzi, skąd pochodzą dane, o które pyta. Po prostu wysyła zapytanie w zakresie, którego potrzebuje, a serwer GraphQL zwraca wynik. Zatem możesz przyjąć, iż schemat to kontrakt między API a klientem, ponieważ zanim klient wyśle jakiekolwiek zapytanie, to zapytanie jest walidowane w oparciu o schemat tego API.
Interakcja klient / serwer w GraphQL
Tutaj chciałabym zastanowić się nad tym, jak w rzeczywistości klient komunikuje z serwerem w trakcie korzystania z GraphQL. Ponieważ nie jestem back-end developerem, o pracy od strony serwerowej opowiem tylko krótko, bez wchodzenia w szczegóły.

Praca w GraphQL w tej chwili jest obsługiwana przez dużą liczbę platform: web, Android, iOS i wiele innych. Klient GraphQL wysyła żądanie uzyskania danych lub ich zmiany, utworzone zgodnie ze schematem, do serwera GraphQL. Z kolei serwer GraphQL to serwer HTTP, z którym powiązany jest schemat GraphQL. Oznacza to, iż wszystkie zapytania otrzymane od klienta i zwrócone odpowiedzi są „przepuszczane” przez ten schemat.
Serwer GraphQL może nie wiedzieć, co zrobić z zapytaniem, jeżeli iż zostanie mu to „wyjaśnione” dzięki specjalnych funkcji. Dzięki nim GraphQL rozumie, jak uzyskać dane dla żądanych pól. Funkcje te są powiązane z odpowiednimi polami i nazywane są resolverami (resolvers). Następnie do klienta zwracana jest odpowiedź, która odzwierciedla żądaną od klienta strukturę danych, zwykle w formacie JSON. I, jeszcze raz to powtórzę, tu możemy pracować z różnymi źródłami danych: bazy danych (relacyjne / NoSQL), wyniki wyszukiwania w sieci, Docker itp.
GraphQL vs REST API – porównanie
Jak już zauważyłam wcześniej, GraphQL został zaprojektowany jako skuteczniejsza alternatywa dla architektury REST do tworzenia interfejsów programowania aplikacji. Dlatego te 2 podejścia mają wspólne cechy:
- Zapewniają jedyny interfejs do przetwarzania danych, które są przechowywane na serwerze zdalnym.
- Najczęściej odpowiedź na zapytanie jest zwracana w formacie JSON.
- Oba podejścia pozwalają na rozróżnienie żądań odczytu i zapisu.
Ale GraphQL ma poniższe zalety w porównaniu z REST:
- Jest zorientowany na klienta, czyli pozwala klientowi uzyskać wymagane przez niego informacje w wymaganym zakresie dokładnie tyle, ile trzeba — ni mniej, ni więcej.
- W przeciwieństwie do REST, potrzebny jest tylko jeden endpoint.
- GraphQL jest silnie typowany, co pozwala wstępnie ocenić poprawność zapytania przed etapem wykonania programu.
- GraphQL zapewnia możliwość łączenia zapytań.
- Zapytania do GraphQL API zawsze zwracają oczekiwany wynik, który jest zgodny ze schematem danych tego API GraphQL.
- Dzięki GraphQL redukujemy dużą ilość danych przesyłanych do klienta, ponieważ klient sam określa, których danych w danej chwili potrzebuje. To znaczy, jeżeli podczas korzystania z REST punkt końcowy określa, jakie dane i w jakiej formie uzyska klient, to podczas korzystania z GraphQL klient nie powinien tworzyć dodatkowych zapytań, zmniejszając tym samym zakres odpowiedzi serwera.
W praktyce korzystanie z GraphQL zwiększa niezależność front-end i back-end developerów. Po uzgodnieniu schematu front-end developerzy nie będą już prosić o tworzenie nowych endpointów lub dodawanie nowych parametrów do istniejących: back-end developerzy raz opisują schemat danych, a front-end developer tworzy zapytania i łączy dane w odpowiedni sposób.
System typów
U podstaw każdego API GraphQL leży opis typów, z którymi możesz pracować i które może zwrócić – jak wspomniano wcześniej, schemat. Ponieważ serwisy GraphQL można pisać w wielu językach, został utworzony uniwersalny GraphQL schema language. Teraz przyjrzymy się głównym typom danych.
Typy obiektowe
Najbardziej podstawowymi typami GraphQL są typy obiektowe, które stanowią obiekt i zestaw pól, które go opisują.
Podam wszystkie przykłady w tym artykule przy użyciu Star Wars API.
Na przykład:
type Planet { id: ID! diameter: Int name: String! population: Float residents: [Person!] }Typy skalarne
Obiekty GraphQL mogą mieć pola różnych typów, ale w końcu muszą być doprowadzone do jednego z obsługiwanych typów skalarnych. Tak więc typy skalarne to są leaf values. Do standardowych typów skalarnych GraphQL należą:
- Int – 32-bitowa liczba całkowita ze znakiem;
- Float – liczba zmiennoprzecinkowa z podwójną precyzją;
- String – linia w UTF-8;
- Boolean – typ logiczny przyjmujący tylko dwie wartości (true lub false);
- ID – typ skalarny reprezentujący unikalny identyfikator dla obiektu, najczęściej używany do ponownego pobrania (wykorzystywany przez cache). Serializowany jest w taki sam sposób jak typ String, ale to, iż dla identyfikatorów został przydzielony osobny typ danych świadczy o tym, iż nie powinien on być używany do wyświetlania klientowi, a jedynie w programach.
Wiele implementacji usługi GraphQL ma również możliwość tworzenia własnych typów skalarnych.
Chciałabym podkreślić, iż w GraphQL można dodać tzw. modyfikatory typu (type modifiers), które wpływają na walidację pól. Bardzo często jest używany modyfikator non-null, który zapewnia, iż podana wartość nigdy nie będzie null (w przeciwnym razie wystąpi błąd). Oznaczone jako !, Na przykład:
id: ID!Innym popularnym modyfikatorem jest List. Można oznakować typ jako List, w takim przypadku oczekuje się, iż tablica takich wartości zostanie zwrócona w to miejsce. Określono symbolem [], na przykład:
films: [Film!]Modyfikatory można łączyć i stosować na dowolnym poziomie zagnieżdżenia.
Argumenty
Są to zestawy par „klucz-wartość”, które są przypisane do określonego pola. Są one przekazywane do serwera i wpływają na sposób pobierania danych dla danego pola. Argumenty mogą być zarówno literałami, jak i zmiennymi. Można je stosować na dowolnych polach, niezależnie od poziomu ich zagnieżdżenia. Muszą być nazwane, mogą być również wymagane lub opcjonalne (jeśli argumenty są opcjonalne, to ich wartość musi być ustawiona domyślnie). Według typu danych wartości argumentów mogą być skalarnymi lub specjalnymi obiektowymi typami input.
Na przykład tutaj argument id jest powiązany z polem Film (w tym przykładzie literał typu ID):
query { Film(id:"1234abcd") { id title characters { name } } }Wyliczeniowy
Specjalny typ skalarny, który jest ograniczony do określonego zestawu wartości.
Przykład:
enum HAIR_COLOR { BLACK BLONDE BROWN GREY }W GraphQL występują również 2 typy abstrakcyjne: union i interface. Mogą być stosowane tylko jako zwracany typ, czyli można uzyskać tylko dane tego typu, ale nie można przekazywać ich do zapytania jako argumentu.
Interfejsy
Typ abstrakcyjny, który zawiera zestaw wymaganych pól, które powinny zawierać typy dziedziczące ten interfejs. Niezastosowanie się do tego spowoduje błąd walidacji schematu. Przykład:
interface Character { id: ID! name: String! } type Droid implements Character { id: ID! name: String! function: String } type Human implements Character { id: ID! name: String! starships: [Starship] }Inny typ abstrakcyjny nie zawiera wymaganych pól. Może być używany tam, gdzie konieczne jest stosowanie rodziny, składającej się z typów, nieposiadających wspólnych pól, na przykład podczas implementacji wyszukiwania lub złożonych subskrypcji.
Przykład:
union SearchResult = Human | Starship | Film ... on Human { name height } ... on Starship { model capacity manufacturer } ... on Film { title episode }Nasuwa się pytanie: po co używać 2 typów abstrakcyjnych? Dlatego, iż te typy mają zasadniczo różne zastosowania. Typy union są używane w tych miejscach schematu, gdzie można przyjąć, iż można tu zwrócić jeden z wymienionych typów. Z kolei interfejsy są używane tam, gdzie można przyjąć, iż typ realizuje ten kontrakt.
Query, mutations, subscriptions
Występują specjalne typy, które definiują typ operacji, którą klient chce wykonać, na przykład pobieranie danych lub ich modyfikowanie. Te typy są punktami wejścia interfejsu API. Każdy GraphQL API powinien zawierać co najmniej jeden query, podczas gdy mutations i subscriptions są opcjonalne. Warto zauważyć, iż pomimo ich specjalnego statusu, te specjalne typy są takie same jak inne typy obiektowe GraphQL. Przyjrzyjmy się bliżej każdemu z tych typów.
Query. Specjalny typ, który zgodnie z definicją reprezentuje zapytania mające na celu pobranie danych (można powiedzieć, iż jest to odpowiednik GET w REST). Ten typ danych jest obowiązkowy dla wszystkich interfejsu API GraphQL. Rozważmy prosty przykład:
query GetAllFilms { allFilms { id title episode } }Jest to podstawowe zapytanie mające na celu pobranie danych o wszystkich filmach (jak sama nazwa wskazuje). Już na tym etapie widoczny jest ważniejszy atut korzystania z GraphQL: pobieramy tylko te dane, które są nam potrzebne w aplikacji (w tym przypadku id, tytuł i numer odcinka filmu). Tak więc w praktyce nie musimy pobierać wszystkich danych, szczególnie iż nie zawsze są one potrzebne, oraz nie musimy wykonywać czasem dość skomplikowanej filtracji, jeżeli można pobrać tylko niezbędne dane i następnie pracować tylko z nimi. Debugowanie i logowanie wymagają podania nazwy zapytania (podczas pracy z niektórymi technologiami, na przykład podczas tworzenia systemu na system Android, zapytania anonimowe w ogóle nie są obsługiwane).
Mutations. Specjalny typ, który w przeciwieństwie do query służy do zmiany danych (można więc powiedzieć, iż jest to odpowiednik POST). Tak, oczywiście, jest to rekomendacja, i w praktyce nie jest zabronione używanie query do zmiany danych, ale mimo to nie warto tego robić. Przecież zasadnicza różnica między query a mutation polega na ich zachowaniu podczas uzyskiwania wyników zapytania: w przypadku query przetwarzanie pól wynikowych będzie wykonywane równolegle, ponieważ nie zmieniają one danych. Z kolei w mutations to się odbywa sekwencyjnie, aby zapewnić integralność danych.
Przykład:
mutation DeleteEpisode { deleteFilm(id: “123456”) { title } }Przykład pokazuje, iż różnic w składni nie ma, zapytanie zaczyna się od nazwy operacji (mutation) i jest też jest zapytaniem nazwanym.
Subscriptions. Najnowszy specjalny typ danych, który służy do wdrożenia systemu publisher/subscriber. Czyli jest to sposób przesyłania danych do klienta, który „nasłuchuje” tych danych w czasie rzeczywistym. W przeciwieństwie do poprzednich 2 typów specjalnych, tutaj odpowiedź serwera nie przychodzi jednorazowo, ale za każdym razem, gdy na serwerze powstają określone warunki, pod warunkiem, iż klient „nasłuchuje” tych danych. Można powiedzieć, iż jest to odpowiednik powiadomień push, które działają w tym samym systemie ze wszystkimi innymi typami zapytań — query i mutations. Dużym plusem takiego podejścia jest to, iż klient również w tym przypadku może zadecydować dzięki argumentów pod jakim warunkiem wynik z serwera powinien trafić do klienta. Przykład:
subscription WaitForNewFilm { Film(filter:{ action:CREATED }) { id title episode characters { name } } }Zwróć uwagę, iż przykład zawiera tylko dane o utworzonym filmie. jeżeli jednak zostaną zmienione informacje o filmie, o ile film zostanie usunięty lub dojdzie do innego zdarzenia, klient nie otrzyma tych danych.
W praktyce GraphQL jest wygodny również dzięki temu, iż podczas korzystania z niego mniej czasu tracimy na wyszukiwanie i naprawianie problemów związanych ze zmianą API. Przykładowo, jeżeli na serwerze zmieniono typ danych określonego pola, usunięto lub dodano nowy typ danych lub nowe pola, to klient API dowie się o tym wcześniej, czyli rozwiązywanie problemu będzie wymagało mniej czasu i wysiłków.
Introspection query i GraphQL Playground
Aby uzyskać informacje o wszystkich typach obsługiwanych przez dane API GraphQL, można uruchomić tzw. introspection query. Domyślnie to zapytanie jest dostępne na debugowanej wersji serwera, ale na production oczywiście powinno być wyłączone. Ponadto praca z interfejsem API jest łatwiejsza dzięki GraphQL Playground, które jest graficznym interaktywnym środowiskiem programistycznym opartym na GraphiQL. Podczas tworzenia i używania Apollo Server, popularnego serwera open-source służącego do opracowań z użyciem GraphQL, Playground staje się dostępny na tym samym endpoint co serwer (na przykład, localhost:4000/playground). I podobnie jak w przypadku introspection query, jest ono dostępne tylko w wersji debugowanej.
Praca z GraphQL API podczas tworzenia systemu dla systemu Android
W końcowej części mojego artykułu chciałabym krótko opisać pracę z GraphQL dla tworzenia systemu dla systemu Androida. Najpopularniejszym serwisem do pracy z GraphQL jest Apollo GraphQL Server. Jest to projekt open-source aktywnie rozwijany przez Apollo GraphQL. Jego wersje nadają się do wielu platform, w tym dla web, iOS i Android. I o tym ostatnim będziemy rozmawiać dalej. Klient Apollo GraphQL dla Android obsługuje na tym etapie wiele podstawowych funkcji, w tym:
- queries, mutations, subscriptions;
- zoptymalizowana pamięć podręczna;
- obieranie plaków;
- własne typy skalarne.
U podstaw jego działania leży generowanie kodu silnie typizowanych modeli wg schematu GraphQL. Domyślnie jest obsługiwane generowanie kodu na Java, generowanie kodu Kotlin może być używane jako funkcja eksperymentalna. JS GraphQL służy do ułatwienia pracy z GraphQL w Android Studio. Jego główne zalety:
- podświetlanie składni, autouzupełnianie, formatowanie kodu;
- możliwość wykonania introspection query w Android Studio;
- obsługiwanie wykonania query, mutations w Android Studio i inne;
- go-to-definition dla plików GraphQL.
Skupmy się na tym, jak uruchomić obsługę GraphQL w projekcie. Najnowsza wersja klienta w chwili pisania artykułu – 1.4.4.
W build.gradle na poziomie aplikacji należy dodać poniższe linie do pracy z Apollo Gradle Plugin:
buildscript { repositories { jcenter() } dependencies { classpath("com.apollographql.apollo:apollo-gradle-plugin:x.y.z") } }W build.gradle na poziomie modułu dodajemy poniższe:
apply plugin: 'com.apollographql.apollo' repositories { jcenter() } dependencies { implementation("com.apollographql.apollo:apollo-runtime:x.y.z") // If not already on your classpath, you might need the jetbrains annotations compileOnly("org.jetbrains:annotations:13.0") testCompileOnly("org.jetbrains:annotations:13.0") }Następnie należy utworzyć katalog w projekcie (module), np. src/main/graphql/com/my_package/, w którym należy umieścić plik schema.json – schemat GraphQL w formacie JSON, uzyskany w wyniku wykonania introspection query tego API, który zostanie wykorzystany w projekcie.
W tym samym katalogu tworzymy pliki zawierające zapytania w formacie .graphql, na przykład queries.graphql. Jedyna uwaga – przy tworzeniu zapytań wszystkie zapytania (query, mutations, subscriptions) powinny być nazwane.

Domyślnie, jak wspomniano powyżej, generujemy kod modeli na Java. jeżeli chcemy uruchomić generowanie kodu Kotlin, dodajemy poniższe:
apollo { generateKotlinModels.set(true) }Następnie wykonujemy polecenie ./gradlew generateApolloSources, żeby modele objęte zapytaniami z katalogu src/main/graphql/com/my_package/ zostały wygenerowane. Podczas tworzenia modeli opartych na typach GraphQL zostaną wygenerowane klasy Java (lub klasy Kotlin), typy pól klas odpowiadają typom skalarnym GraphQL. Podczas korzystania z typu ID w GraphQL, wygenerowana klasa używa typu String. W razie potrzeby można również dodać własne typy skalarne oraz zdefiniować sposób ich zmiany (ich mapowania). Klasa ApolloClient służy do pracy z serwerem (na przykład do wykonywania zapytań i ich konfigurowania, ustawień subscriptions) oraz do pracy z pamięcią podręczną. Przykład utworzenia i konfiguracji:
val apolloClient : ApolloClient = ApolloClient.builder() .serverUrl(ENDPOINT) .subscriptionTransportFactory( WebSocketSubscriptionTransport.Factory(SUBSCRIPTIONS_ENDPOINT, okHttp)) .okHttpClient(okHttp) .build()Teraz rozważymy, w jaki sposób możemy wykonać podstawowe typy operacji GraphQL. Klient Apollo GraphQL obsługuje zarówno standardowe wykonywanie operacji z wykorzystaniem funkcji callback, jak i RxJava2 i coroutines, w tym celu planowane jest uruchomienie poszczególnych zależności Gradle.
Przypuśćmy, iż chcemy wykonać 2 zapytania, zawierające ten sam typ danych GraphQL Film i możliwość wyboru tych samych pól w nim:
query GetFilmNotOptimized($id:ID!) { Film(id:$id) { id title director episodeId planets { id name diameter population climate } } } query GetFilmsNotOptimized { allFilms { id title director episodeId planets { id name diameter population climate } } }Tak to będzie wyglądało w projekcie. Pierwsze zapytanie:
apolloClient.query( GetFilmNotOptimizedQuery.builder() .id(id) .build() ).enqueue(object : ApolloCall.Callback<GetFilmNotOptimizedQuery.Data>() { override fun onFailure(e: ApolloException) { /* Response to exception */ } override fun onResponse(response: Response<GetFilmNotOptimizedQuery.Data>) { val filmType = response.data()?.Film()?.__typename() val filmClassName = response.data()?.Film()?.javaClass?.simpleName Log.i("ApolloStarWars", """GetFilmNotOptimizedQuery: film type = $filmType, film Java class = $filmClassName""") //result: GetFilmNotOptimizedQuery: film type = Film, film Java class = Film } })I drugie zapytanie:
apolloClient.query( GetFilmsNotOptimizedQuery() ).enqueue(object : ApolloCall.Callback<GetFilmsNotOptimizedQuery.Data>() { override fun onFailure(e: ApolloException) { /* Response to exception */ } override fun onResponse(response: Response<GetFilmsNotOptimizedQuery.Data>) { val filmType = response.data()?.allFilms()?.first()?.__typename() val filmClassName = response.data()?.allFilms()?.first()?.javaClass?.simpleName Log.i("ApolloStarWars", """GetFilmsNotOptimizedQuery: films type = $filmType, film Java class = $filmClassName""") //GetFilmsNotOptimizedQuery: films type = Film, film Java class = AllFilm } })Tutaj możesz zobaczyć, iż Apollo GraphQL generuje 2 różne typy Java dla tego samego typu GraphQL, choćby jeżeli wybrane są te same pola. W tym przypadku można skorzystać z fragmentów GraphQL — struktur wielokrotnego użytku, które pozwalają definiować zestawy pól oparte na dowolnym typie GraphQL, i uwzględniać je w zapytaniach. Na przykład poprzednie zapytania można zoptymalizować dzięki poniższych fragmentów:
fragment PlanetFragment on Planet { id name diameter population climate } fragment FilmFragment on Film { id titleZapytania z kolei będą wyglądać znacznie prościej, a mianowicie tak:
query GetFilm($id:ID!) { Film(id:$id) { ...FilmFragment } }To w końcu pozwoli uzyskać ten sam typ Java w wyniku wykonywania różnych zapytań, które zostaną wygenerowane jednorazowo dla wszystkich fragmentu GraphQL.
Obsługa RxJava2 i Kotlin Coroutines
I na koniec jeszcze kilka słów o RxJava2 i Coroutines.
Aby uwzględnić w projekcie obsługę RxJava2 należy dodać poniższy import do build.gradle na poziomie modułu:
implementation 'com.apollographql.apollo:apollo-rx2-support:x.y.z'On zawiera zestaw funkcji rozszerzającej (Kotlin) i klasę Rx2Apollo (Java) do konwersji ApolloCall na Observable. Przypadek użycia:
apolloClient.rxQuery( GetFilmsQuery() ).singleElement() .toSingle() .map { response -> return@map response.data()?.allFilms()?.map { film -> film.fragments().filmFragment().toFilm() }?: listOf() }Podobnie, do obsługi Coroutines, dodajemy następujący import:
implementation 'com.apollographql.apollo:apollo-coroutines-support:x.y.z'Tu zostały ujęte funkcje rozszerzające do konwersji ApolloCall w Flow, w Deferred i Channel.
apolloClient.query( GetFilmsQuery() ).toDeferred() .await() .data()?.allFilms()?.map { film -> film.fragments().filmFragment().toFilm() }?: listOf()Wnioski
Reasumując, chciałabym zauważyć, iż GraphQL to koncepcja stworzenia API, która zapewnia słaby kontrakt między klientem a serwerem. Nie ulega wątpliwości, iż po pojawieniu się tej technologii nie musimy całkowicie odrzucać architektury REST.
Co więcej, z mojego punktu widzenia, jeżeli struktura danych w projekcie nie jest skomplikowana, nie koniecznie powinniśmy przejść na GraphQL, decyzja należy tylko od developerów. Ale to podejście naprawdę ułatwia pracę z dużymi zbiorami heterogenicznych danych. Najważniejsze, aby pamiętać, iż GraphQL nie polega na przetwarzaniu wszystkiego, co było wcześniej, zatem każdy powinien wybrać najbardziej odpowiadające podejście wg własnego uznania. Ale uważam, iż GraphQL to technologia, z którą zdecydowanie warto się zapoznać.
Jako programista Android chętnie korzystam z tej technologii z wielu powodów. Po pierwsze, dzięki temu, iż aplikacja kliencka współdziała z wygenerowanymi klasami Java lub Kotlin; po drugie, przez to, iż nie musimy filtrować danych na kliencie i po trzecie możemy korzystać nie tylko z zapytań mających na celu pobranie danych i ich modyfikowanie, ale także z subscriptions (można powiedzieć, odpowiedniki powiadomień push) w jednym systemie bez konieczności stosowania i konfigurowania serwisów zewnętrznych itd.
Aby uzyskać więcej informacji na ten temat, a także dowiedzieć się o zaawansowanych funkcjach na przykład, o pracy z pamięcią podręczną), podaję niżej wykaz przydatnych linków:
- Oficjalna strona GraphQL;
- Apollo GraphQL Server;
- Repozytorium GitHub Apollo GraphQL klienta dla Android;
- Repozytorium GitHub zawierający przykłady kodu z niniejszego artykułu .