Marcin Rzepecki
Marcin RzepeckiJava Developer @ Team Connect

REST vs GraphQL - porównanie

Sprawdź porównanie REST i GraphQL pod kątem tworzenia i utrzymywania API.
12.10.20216 min
REST vs GraphQL - porównanie

Od czasu swojej premiery GraphQL zyskał bardzo duże uznanie w świecie twórców oprogramowania. Dzisiaj mamy rok 2021 i coraz więcej firm decyduje się na wdrożenie tej technologii, rezygnując z możnaby powiedzieć standardowego już podejścia, jakim jest REST API. Czy aby jednak słusznie? Patrząc na taki rozwój sytuacji, każdy z nas zadaje sobie te pytania: "Co sprawia, że GraphQL jest taki atrakcyjny? Jakie są jego mocne strony, bijące na głowę REST? Jakie są jego słabe strony, bo w końcu jakieś musi mieć? I wreszcie - czy w moim projekcie jest miejsce na rewolucję GraphQL?".

Sprawdźmy, jak obie technologie radzą sobie na różnych polach istotnych z punktu widzenia tworzenia i utrzymywania API!

Runda 1 - Pobieranie danych

Koncepcja stylu REST opiera się na implementacji tzw. endpointów, czyli punktów dostępu do określonych zasobów. To serwer określa ilość endpointów, ich postać oraz wymagane parametry żądania i strukturę odpowiedzi. Część klientów takiej usługi niekoniecznie potrzebuje wszystkich danych zwracanych w ich reprezentacji. Przykładowo, klient usługi udostępniającej dane o użytkownikach chce pobrać nazwy ich kont. Jako wynik przetworzenia takiego żądania otrzymujemy:

[
    {
        "username": "lorem",
        "posts": [...],
        "notes": [...],
        ...
    }
]


Mamy do czynienia z overfetchingiem - klient jest zmuszony odebrać całą zawartość odpowiedzi, mimo iż jej całej nie potrzebuje, co tylko zwiększa czas jej przesłania i w konsekwencji spowalnia komunikację.

W innym scenariuszu dane otrzymywane z jednego punktu dostępu mogą być niewystarczające, przez co trzeba wysłać żądania do innych punktów tego API. Przykładowo, punkt dostępu do danych użytkowników zwraca informację o postach przez nich utworzonych jako listę ich identyfikatorów. Taką sytuację nazywamy underfetchingiem i wymusza na kliencie pobranie kompletu danych z innych zasobów.

Zupełnie inne podejście oferuje GraphQL. Klienci wysyłają zapytanie (query) w żądaniu o to, co chcą pobrać. Klient określa dokładną strukturę odpowiedzi i dostaje tylko ten zestaw danych, których potrzebuje. Tak mogłoby wyglądać zapytanie o nazwy kont użytkowników:

{
    users {
        username
    }
}


A tak zapytanie o zawartość postów użytkowników:

{
    users {
        posts {
            title,
            content,
            date
        }
    }
}


Klient może sam określać, których z udostępnionych danych w danej chwili potrzebuje. Nie ma over- i underfetchingu. Klient dostaje to, czego chce.

Runda 2 - Wydajność

Często potrzebujemy uzyskać dane z wielu różnych zasobów, źródeł czy API. Korzystając z REST API jesteśmy zmuszeni odpytywać wiele różnych zasobów w celu uzyskania pełnego zestawu danych. Prowadzi to często do wysłania n+1 żądań co stanowi wydajnościowy punkt zapalny komunikacji REST.

Użycie GraphQL-a pozwala na zminimalizowanie liczby zapytań potrzebnych do otrzymania kompletu danych. Podejście to stanowi podstawy filozofii GraphQL - traktuje ona dane jako graf encji powiązanych zależnościami. Encje są łączone w odpowiedzi w zależności od postaci zapytania wysłanego przez klienta. Zmniejsza to narzut komunikacji między stronami i zwiększa jej wydajność. 

Runda 3 - Caching

Cachowanie danych w dużym skrócie polega na przechowaniu ich w miejscu szybszego ponownego do nich dostępu, w porównaniu do pierwotnego źródła. Najpowszechniejsza implementacja REST oparta na protokole HTTP radzi sobie z tym zagadnieniem bez problemu, gdyż cachowanie jest częścią specyfikacji tego protokołu. Korzystając z REST masz dostęp do cachowania odpowiedzi w przeglądarce, proxy pośredniczących czy też frameworku webowego samego serwera.

GraphQL ze względu na swoje podejście nie podążą za specyfikacją cachowania protokołu HTTP (mimo że są implementacje GraphQL oparte o protokół HTTP). Utrudnia to fakt istnienia jednego punktu dostępu do danych i tworzeniu zapytań w oparciu o język zapytań niezrozumiały dla przeglądarki. Na szczęście, istnieje kilka bibliotek wspomagających implementację cache po stronie serwera GraphQL: FlacheQL, Relay czy Apollo GraphQL - warto się z nimi zapoznać!

Runda 4 - Walidacja

REST pozostawia zadanie walidacji żądań po stronie programisty. Tutaj możemy skorzystać z ogromu narzędzi wspierających rozwijanie takiego API. Jest to pewna cena elastyczności, która w czasie zaproponowania REST była cechą bardzo atrakcyjną w stosunku do sztywnego kontraktu wyznaczonego przez protokół SOAP. Jednakże taka elastyczność wymaga stworzenia dobrze opisującej możliwości API dokumentacji, a to nie zawsze idzie w parze. 

GraphQL wyciąga wnioski z przeszłości i wprowadza system typów danych. Opiera się to o stworzenie schematu API. Taki schemat opisuje dokładnie, jak klient może uzyskać dane. Schemat zawiera opis typów danych wraz z ich właściwościami. Schemat taki jest wykorzystywany przy walidacji żądań, która jest wbudowana w implementację GraphQL.

Runda 5 - Obsługa błędów


Dojrzałe REST API w pełni do swoich potrzeb korzysta z podzbioru kodów HTTP umieszczanych w nagłówku odpowiedzi do klienta. Na tej podstawie jest on w stanie stwierdzić, co poszło nie tak w komunikacji z serwerem i jak sobie z tym poradzić. Najprawdopodobniej spotkasz się z takim znaczeniem poniższych kodów błędów:

  1. 200 OK- przetworzyłem dane i zwracam wynik w odpowiedzi,
  2. 400 Bad Request- wygląda na to, że przesłane przez Ciebie żądanie jest niepoprawne, spróbuj je poprawić,
  3. 404 Not Found- przepraszam, ale nie mogę znaleźć zasobu, którego szukasz, spróbuj się poprawić,
  4. 500 Internal Server Error- oops, coś poszło nie tak po mojej stronie!


GraphQL niestety odbiega od tak przyjętego standardu. Generalnie, w użyciu pozostają kody HTTP 5xx, aby sygnalizować komunikacyjne problemy po stronie serwera oraz HTTP 4xx w celu poinformowania o nieprawidłowym żądaniu HTTP klienta. Pozostałe problemy mogą być zwracane z kodem 200, co niekoniecznie musi oznaczać poprawne przetworzenie zapytania. Co więcej, domyślna implementacja GraphQL-a dostarcza informację o błędzie w odpowiedzi, ale ta odpowiedź jest najczęściej niezrozumiała dla klienta i zaciemnia sposób użycia API. Jak sobie poradzić z takim problemem? Warto dodać błędy jako część schematu. Dzięki temu klient ma szansę dostać lepsze wyjaśnienie zaistniałej sytuacji:

error:{
        id:'121'
        title: 'Nazwa błędu'
        message: 'Coś poszło nie tak'    
    }

Runda 6 - Rozwój produktu

Wyobraźmy sobie sytuację, w której rozwijamy system podzielony na warstwę frontendową i backendową. Naszym zadaniem jest wybranie architektury komunikacji sieciowej pomiędzy tymi warstwami. REST najczęściej w takim scenariuszu blokuje pracę zespołu frontendowego jeśli zależy ona od postaci danych odbieranych z API. Użycie GraphQL całkowicie likwiduje problem, gdyż klient utrzymuje informację w postaci schematu o tym, jakie dane może pobrać - na tej podstawie zespół frontendowy może przygotować odpowiednie zapytania i je zamockować, zrównoleglając pracę z zespołem backendowym.

Czasami również nasze API obsługuje aplikację mobilne i witryny internetowe - zastosowanie GraphQL sprawia, że nie musimy dostarczać dla każdej takiej aplikacji dedykowanego interfejsu - wystarczy, że wyślą one odpowiednie zapytania! Jedynym warunkiem uzyskania tak wygodnej sytuacji jest ustalenie postaci schematu GraphQL, a sprawa ta jest zdecydowanie uproszczona po obu stronach barykady, gdyż obie strony operują na tych samych obiektach, w przypadku REST jest to utrudnione, gdyż często endpointy zwracają reprezentację jakiegoś zasobu, będącego nieczęsto projekcją złożoną z wielu obiektów!

Runda 7 - Odporność na zmiany wymagań

W procesie wytwarzania oprogramowania możemy natrafić na wiele niewiadomych, ale jedna rzecz jest zawsze pewna: wymagania ulegają zmianie. W takim przypadku GraphQL jest technologią zdecydowanie bardziej odporną na taki stan rzeczy - w najprostszych przypadkach wystarczy, że aplikacje klienckie wyślą inne zapytanie o właściwości czy obiekty pojawiające się na schemacie. Sprawa już tak dobrze nie wygląda w przypadku RESTa: dobrą praktyką jest unikanie overfetchingu, a zatem endpointy powinny udostępniać zasoby tylko takie, jakie są potrzebne w danej chwili. Niekiedy każda taka zmiana wymagań klienta wymusza wydanie nowej wersji interfejsu API.

Podsumowanie

Ciężko jest mówić o definitywnym zwycięstwie jednej technologii nad drugą. Niewątpliwie GraphQL jest technologią przełomową, zmieniającą podejście do serwowania danych. Klient otrzymuje dokładnie to, co chce, w najwygodniejszy dla niego sposób - z użyciem jednego zapytania. Brak natywnego wsparcia dla cacheowania, autoryzacji, paginacji i filtrowania może nadal kierować programistów do pozostania w bezpiecznej przystani. Myślę jednak, że już dzisiaj z powodzeniem możemy starać się maksymalizować korzyści płynące z obu technologii - GraphQL świetnie radzi sobie z dostarczaniem treści różnym klientom.

Jeżeli zależy nam na minimalizacji ruchu sieciowego i na niezależności rozwijanych produktów to celowałbym w zastosowanie GraphQLa jako API do użytku wewnętrznego. Nadal polecałbym użycie REST w przypadku tworzenia publicznych API ze względu większą dojrzałość tej technologii w zakresie wsparcia narzędzi np. do monitorowania aktywności API. Jeżeli nie jesteś pewny, w jaki do końca sposób dane będą wykorzystywane i w jaki sposób przetwarzane to wybierz GraphQL i skorzystaj z jego elastyczności. W innym przypadku REST powinien być wystarczający, nienarzucający dodatkowego narzutu technologicznego np. wbudowaną walidacją zapytań. 

<p>Loading...</p>