Dwa podejścia do komunikacji między frontendem a backendem

Projektując aplikację webową, prędzej czy później stajemy przed pytaniem: jak frontend ma pobierać dane z backendu? Przez lata standardem było REST API. Od 2015 roku, gdy Facebook opublikował specyfikację GraphQL, pojawiła się poważna alternatywa. Oba podejścia mają swoje mocne strony i ograniczenia — kluczem jest zrozumienie, kiedy które sprawdzi się lepiej.

REST API — sprawdzony standard

REST (Representational State Transfer) to styl architektoniczny oparty na zasobach i metodach HTTP. Każdy zasób (użytkownik, produkt, zamówienie) ma swój endpoint, a operacje CRUD mapowane są na metody HTTP:

GET    /api/users          — lista użytkowników
GET    /api/users/42       — szczegóły użytkownika o ID 42
POST   /api/users          — utworzenie nowego użytkownika
PUT    /api/users/42       — aktualizacja użytkownika
DELETE /api/users/42       — usunięcie użytkownika

To intuicyjny model: URL identyfikuje zasób, metoda HTTP określa operację, kody statusu (200, 201, 404, 500) informują o wyniku. REST działa z każdym klientem HTTP — przeglądarką, aplikacją mobilną, narzędziem wiersza poleceń, nawet curl.

Mocne strony REST:

  • Prostota — opiera się na standardowych mechanizmach HTTP, które każdy programista zna
  • Cache — odpowiedzi GET można cachować za pomocą standardowych nagłówków HTTP (ETag, Cache-Control)
  • Narzędzia — ogromny ekosystem: Swagger/OpenAPI do dokumentacji, Postman do testowania, middleware do autoryzacji
  • Skalowalność — bezstanowość REST sprawia, że łatwo skalować horyzontalnie (load balancing)
  • Dojrzałość — sprawdzony w produkcji przez dekady, dobrze rozumiany przez zespoły

GraphQL — elastyczność zapytań

GraphQL to język zapytań i runtime stworzony przez Facebook. Zamiast wielu endpointów mamy jeden (/graphql), a klient precyzyjnie określa, jakie dane chce otrzymać:

query {
  user(id: 42) {
    name
    email
    orders(last: 5) {
      id
      total
      items {
        name
        price
      }
    }
  }
}

Jedno zapytanie pobiera użytkownika, jego ostatnie 5 zamówień i pozycje w każdym zamówieniu. W REST wymagałoby to 2-7 osobnych zapytań HTTP (użytkownik, zamówienia, pozycje każdego zamówienia) lub stworzenia specjalnego endpointu.

Mocne strony GraphQL:

  • Precyzyjne pobieranie danych — klient dostaje dokładnie to, o co prosi, nic więcej (eliminuje over-fetching)
  • Jedno zapytanie zamiast wielu — złożone dane z wielu powiązanych zasobów w jednym żądaniu (eliminuje under-fetching)
  • Silne typowanie — schemat definiuje dostępne typy, pola i relacje, co daje doskonałą dokumentację i walidację
  • Introspection — klient może odpytać serwer o dostępny schemat, co ułatwia eksplorację API
  • Ewolucja bez wersjonowania — dodawanie nowych pól nie psuje istniejących klientów, bo każdy klient pyta tylko o to, czego potrzebuje

Problem over-fetching i under-fetching

Te dwa problemy to główna motywacja za GraphQL.

Over-fetching — REST endpoint zwraca więcej danych niż klient potrzebuje. Endpoint /api/users/42 zwraca 30 pól (imię, email, adres, historię, preferencje...), a frontend potrzebuje tylko imienia i avatara. Cały nadmiar jest przesyłany przez sieć i przetwarzany — szczególnie bolesne na wolnych połączeniach mobilnych.

Under-fetching — potrzebujemy danych z wielu zasobów, ale REST wymaga osobnego zapytania na każdy. Strona profilu wymaga danych użytkownika (/api/users/42), jego postów (/api/users/42/posts), znajomych (/api/users/42/friends) i powiadomień (/api/notifications). Cztery zapytania HTTP, każde z osobnym round-tripem do serwera.

GraphQL rozwiązuje oba problemy jednym precyzyjnym zapytaniem. Ale czy to zawsze konieczne?

Kiedy REST jest lepszym wyborem

REST sprawdza się najlepiej, gdy:

  • API jest publiczne — REST jest uniwersalnie zrozumiały, łatwy do dokumentowania i testowania. Większość integracji między firmami opiera się na REST
  • Operacje są proste — CRUD na kilku zasobach nie wymaga elastyczności GraphQL
  • Cache jest ważny — HTTP cache dla REST jest trywialny (GET na URL = klucz cache). W GraphQL wszystkie zapytania to POST na ten sam endpoint, co komplikuje cachowanie
  • Zespół jest mały — REST nie wymaga dodatkowej warstwy (serwer GraphQL, schemat, resolwery) i jest szybszy we wdrożeniu
  • Serwer renderuje HTML — gdy backend generuje strony HTML (jak w naszych projektach w Go), REST endpoints dla danych zewnętrznych są naturalnym wyborem

Kiedy GraphQL jest lepszym wyborem

GraphQL sprawdza się, gdy:

  • Frontend jest złożony — aplikacje SPA (React, Vue) z wieloma widokami potrzebującymi różnych kombinacji danych
  • Wielu klientów — aplikacja webowa, mobilna i desktopowa potrzebują tych samych danych, ale w różnych konfiguracjach
  • Złożone relacje — dane są mocno powiązane (graf) i różne widoki potrzebują różnych głębokości tych relacji
  • Szybka iteracja frontendu — frontend może dodawać i usuwać pola bez zmian w backendzie
  • Mikroserwisy — GraphQL może służyć jako warstwa agregacyjna łącząca dane z wielu mikroserwisów

Wady GraphQL, o których się rzadziej mówi

GraphQL nie jest panaceum. Warto znać jego ograniczenia:

  • Złożoność serwera — definiowanie schematu, resolverów i optymalizacja zapytań (problem N+1) wymaga dodatkowej pracy
  • Trudniejsze cachowanie — brak standardowego HTTP cache wymusza użycie bibliotek (Apollo Client) lub CDN ze wsparciem dla GraphQL
  • Bezpieczeństwo — klient może tworzyć dowolnie głębokie i kosztowne zapytania. Potrzebne są limity głębokości, złożoności i rate limiting
  • Monitoring — wszystkie żądania to POST na /graphql, co utrudnia analizę logów i monitorowanie poszczególnych operacji
  • Przesyłanie plików — GraphQL nie ma natywnego wsparcia dla upload plików, wymagane są obejścia (multipart lub osobny endpoint REST)

Podejście hybrydowe

W praktyce wiele projektów łączy oba podejścia. REST dla prostych operacji CRUD i integracji zewnętrznych, GraphQL dla złożonych widoków frontendowych wymagających elastyczności. To pragmatyczne rozwiązanie, które wykorzystuje mocne strony obu technologii.

Inną opcją wartą rozważenia jest tRPC (dla projektów w TypeScript) lub proste REST z dobrze zaprojektowanymi endpointami, które zwracają zagnieżdżone dane tam, gdzie to potrzebne. Czasem wystarczy dodać parametr ?include=orders,items do REST endpoint, zamiast wdrażać cały stos GraphQL.

Podsumowanie

REST API to sprawdzony, prosty i uniwersalny standard, który sprawdza się w większości projektów — szczególnie w małych i średnich aplikacjach, publicznych API i projektach z server-side rendering. GraphQL to potężne narzędzie dla złożonych frontendów z wieloma klientami i skomplikowanymi relacjami danych, ale niesie ze sobą dodatkową złożoność.

Wybór powinien wynikać z realnych potrzeb projektu, a nie z trendów. Jeśli REST z dobrze zaprojektowanymi endpointami rozwiązuje Twoje problemy — to jest właściwy wybór. Jeśli walczysz z over-fetchingiem, mnożącymi się endpointami i różnymi potrzebami wielu klientów — GraphQL może znacząco uprościć architekturę.