Zanim przejdę do dokładnego, subiektywnego omówienia różnic pomiędzy tymi dwoma podejściami zapytań serwera / backendu warto zaznaczyć, że żadna z tych technologii nie jest lepsza lub gorsza od drugiej. To są zupełnie 2 różne technologie i podejścia do budowy systemu aplikacji webowej.
Pierwsze różnice ...
REST to architektoniczny koncept budowy systemów internetowych, nie ma oficjalnych narzędzi oraz specyfikacji . Zaprojektowany z myślą aby rozłączyć (decouple) API od klienta (frontu). W przeciwieństwie do GraphQl może nie tylko posługiwać się HTTP, jego zadaniem jest budowa stabilnej i trwałej architektury systemu.
GraphQL to język zapytań "query language", specyfikacji i narzędzi zbudowanych z myślą o optymalizacji dostarczania danych poprzez jeden endpoint HTTP.
Jedną z głównych zasad konwencji REST, jest to aby używać wszystkich dostępnych protokołów. REST używa HTTP content-types, caching, status codes. GraphQL używa swoich własnych konwencji
W poście postaram się pokazać wam właśnie te różnice w podejściu do budowy aplikacji. Warto pamiętać, że pod koniec dnia jaka technologia jest lepsza zależy od tego co robicie ze swoją aplikacją, dla niektórych rzeczy GraphQL będzie lepszy, dla innych REST, dla jeszcze innych obydwa podejścia mogą okazać się tak samo frustrujące.
Czy API to tylko transfer danych ?
Jedną z podstawowych zadań REST API to tzw operacje CRUD (create, read, update, delete) poprzez plik JSON. Jedną zalet REST jest łatwe i szybkie przesyłanie plików
Przykładowy przesył pliku jpeg
POST /avatars HTTP/1.1
Host: localhost:3000
Content-Type: image/jpeg
Content-Length: 284
raw image content
Korzystając z protokołu HTTP (a zatem i REST), programiści mogą obsługiwać żądania aplikacji / json na tym samym punkcie końcowym, można także oferować przesyłanie na podstawie adresu URL:
POST /avatars HTTP/1.1
Host: localhost:3000
Content-Type: application/json
{
"image_url" : "https://spaceout.pl/pic.png"
}
Gdybyśmy mówili o przesyłaniu filmów lub innych dużych plików zaproponowałbym inne podejście i miałbym dedykowaną usługę, która obsługuje przesyłanie, pozostawiając główny interfejs API akceptujący tylko metadane; tytuł, opis, tagi itp.
W przypadku GraphQL takie podejście to nasz jedyny wybór, ponieważ możesz komunikować się z GraphQL tylko w zakresie pól:
POST /graphql HTTP/1.1
Host: localhost:3000
Content-Type: application/graphql
mutation addAvatar {
addProfileImageFromUrl(image_url: "https://spaceout.pl/pic.png") {
id,
image_url
}
}
Niektórzy twierdzą, że jest to bardziej „czyste”, ale zmuszenie do stworzenia innej usługi jest przesadą w przypadku mniejszych obrazów, szczególnie na początku projektu.
Innym podejściem jest przesyłanie bezpośrednio np do Amazon S3, wymuszając zależność od klientów i potencjalnie pozwalając na wyciek tokenów.
Jednym z obszarów, w którym GraphQL wyróżnia się, jest niezwykle łatwe monitorowanie wykorzystania pola na poziomie technicznym. Klienci GraphQL są zmuszeni do określenia pól, które mają zostać zwrócone w zapytaniu:
POST /graphql HTTP/1.1
Host: localhost:3000
Content-Type: application/graphql
{
spaceships(id: "123") {
length,
width,
power
}
}
Śledzenie tego byłoby trywialne, ale interfejs API REST działa nieco inaczej. Podczas gdy wszystkie interfejsy API REST udostępniają podstawowy punkt końcowy za pośrednictwem/spaceships/123
, nie wszystkie interfejsy API oferują rzadkie zestawy pól: /spaceships/123?fields=length,width,intelligence
.
Jeśli klient interfejsu API REST wywołuje /spaceships/123
, może używać dowolnego pola w tej odpowiedzi. Wyobraź sobie, że API planuje pozbyć się power
, skąd programiści API wiedzą, którzy klienci używają tego pola?
Najczęściej rozwiązuje się ten problem przez utworzenie nowego punktu końcowego / /getSpaceships2
(lub /v2/spaceships/123
) i poleceniu innym użytkownikom korzystania z tego nowego.
Innym podejściem byłoby utworzenie nowej wersji zasobu, co oznacza, że zamiast wywoływać GET
za pomocą Accept: application/vnd.spaceout.pl+v1+json
, teraz muszą używać Accept: application/vnd.spaceout.pl+v1+json
GraphQL stawia na wydajność
GraphQL jest zawsze najmniejszym możliwym żądaniem, podczas gdy REST zazwyczaj domyślnie zwraca cały endpoint.
Kiedy aplikacja chce banana, REST zwraca Goryla z dżunglą, trzymającego banana"
Nawet jeśli interfejs API REST domyślnie zwraca tylko podstawowe częściowe, nadal domyślnie przesyłanych jest więcej bitów niż w przypadku metody GraphQL. Jeśli klient potrzebuje pola, zażąda go, a jeśli interfejs API doda nowe pole, klienci go nie otrzymają.
REST sprawia, że buforowanie jest łatwiejsze na wszystkich poziomach
W interfejsie API opartym na punkcie końcowym klienci mogą korzystać z buforowania HTTP, aby uniknąć ponownego pobierania zasobów oraz do identyfikowania, kiedy dwa zasoby są identyczne. Jednak w GraphQL nie ma elementu podstawowego podobnego do adresu URL, który zapewnia ten globalnie unikalny identyfikator dla danego obiektu. Dlatego najlepszą praktyką dla API jest ujawnianie takiego identyfikatora do użycia przez klientów. - Źródło: graphql.org
REST przez HTTP wykorzystuje cały stos konwencji HTTP, które sprawiają, że istniejący klienci HTTP, serwery proxy pamięci podręcznej HTTP itp. Działają łatwo, zapewniając korzyści zarówno klientom API, jak i serwerom API, ale z GraphQL… trudnym. Zreorganizuj swoje magazyny danych, użyj kilku Redis i mam nadzieję, że klienci też będą buforować.
GraphQL to język zapytań
Bardzo podstawową różnicą jest tutaj to, że tylko jeden z nich jest językiem zapytań. Interfejsy API REST są często tworzone początkowo proste, a następnie z czasem wprowadzane są coraz więcej funkcji podobnych do języka zapytań.
Najbardziej rozsądnym sposobem dostarczenia argumentów dla zapytań w REST jest umieszczenie ich w ciągu zapytania. Może? ?status=active
, aby filtrować według statusu, a następnie prawdopodobnie sort=created
, ale klient potrzebuje kierunku sortowania, więc dodaje się sort-dir=desc
.
Niektóre używane interfejsy API kończą się także argumentami w ciągu zapytania, które nie są związane z filtrowaniem, a bardziej opcjami.
W przykładzie GraphQL określają jednostkę, w której mają być zwrócone height
:
{
human(id: "1000") {
name
height(unit: FOOT)
}
}
{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 5.6430448
}
}
}
Z REST przy takim zapytaniu mamy trochę bałagan; ?status=active
jako filtr, ?unit=foot
jako dodatkowa opcja wyświetlania danych. Nasze API /unit=foot?status=active
, szybko korzystanie z takich rozwiązań kończy się bólem głowy bez dokumentacji czy jakieś konwencji w budowaniu naszego API.
GraphQL wyprzedza interfejsy API REST, które próbują ręcznie wprowadzić własną funkcjonalność języka zapytań. GraphQL zapewnia składnię języka zapytań oraz zestawy SDK, potrzebne front-end developerowi. Jeżeli planujesz wysoce konfigurowalny interfejs API, jest to kolejny powód do wybrania GraphQL.
Dostosowanie API
Inną kwestią związaną z dostosowywaniem naszego API, jest to, kiedy zaoferować uwzględnione relacje a kiedy użyć innego punktu końcowego. Może to być trudny wybór projektowy, interfejs API powinien być elastyczny i wydajny.
Zaczynasz od /users?include=comments,posts
a tu jeszcze profile, settings ... i więcej informacji które potrzebuje klient. Tutaj znowu pojawia się problem banana z gorylem i dżungla. Czasami potrzebne są tylko szczątkowe informacje potrzebne z każdego obiektu.
GraphQL nie tylko usuwa to pytanie projektowe, ale wymusza podejście uwzględniające i zmusza do rozważenia wydajnych sposobów pobierania tych danych. Jest to duża wygrana dla GraphQL, będzie on zarówno wydajny, jak i spójny.
GraphQL nie pomoże w zapytaniach do bazy danych, więc nadal musisz dostroić te indeksy i buforować fragmenty danych w inteligentny sposób.
Dlaczego nie skorzystać z obu?
Obydwa podejścia mają swoje wady i zalety, nie znaczy jednak, że musimy się decydować na tylko jedno z nich. REST API świetnie posłuży do przesyłania zdjęć, potwierdzania tokenów, resetu hasła ..., GraphQL jest wydajnym klientem dostarczającym treść i dane potrzebne dla każdego zaplanowanego widoku strony internetowej czy aplikacji.