GraphQL — to język zapytań do API i środowisko wykonawcze do realizacji tych zapytań, opracowany przez Facebook w 2012 roku i udostępniony jako open source w 2015. W przeciwieństwie do REST, gdzie serwer określa strukturę odpowiedzi, GraphQL pozwala klientowi dokładnie wskazać, jakie dane są mu potrzebne, całkowicie eliminując problemy overfetchingu i underfetchingu. Według State of JavaScript Survey (2025), GraphQL używa 35% ankietowanych programistów, a wśród dużych firm wdrożyły go GitHub, Shopify, Airbnb i The New York Times. GraphQL obsługuje trzy typy operacji: query (odczyt), mutation (zapis) i subscription (aktualizacje w czasie rzeczywistym przez WebSocket).
Najważniejsze
GraphQL — to specyfikacja i środowisko wykonawcze dla API, które daje klientowi pełną kontrolę nad otrzymywanymi danymi. Opracowana przez inżynierów Facebooka w celu rozwiązania problemów mobilnej aplikacji News Feed, specyfikacja została opublikowana jako otwarty standard w 2015 roku. Od 2018 roku GraphQL znajduje się pod zarządem GraphQL Foundation przy wsparciu Linux Foundation i takich firm jak Apollo, AWS, GitHub, SAP i innych.
W przeciwieństwie do REST, gdzie każdy endpoint zwraca ustaloną strukturę danych, GraphQL używa pojedynczego endpointu, który przyjmuje ciąg zapytania. Klient opisuje w zapytaniu, które pola są mu potrzebne, a serwer zwraca dokładnie je. Na przykład zapytanie { user(id: “1”) { name email } } zwróci tylko name i email użytkownika, bez zbędnych pól takich jak address, phone czy createdAt, które trzeba by było pobierać w REST.
GraphQL nie jest powiązany z żadną konkretną bazą danych ani językiem. Specyfikacja określa tylko format zapytań i odpowiedzi. Istnieją implementacje serwerów w Node.js (graphql-js, Apollo Server), Kotlin (graphql-kotlin, Netflix DGS Framework), Python (Graphene, Strawberry), Ruby (graphql-ruby) i innych językach. Biblioteki klienckie są dostępne dla wszystkich głównych platform, w tym Apollo Client dla iOS, Androida i sieci Web.
Architektura GraphQL składa się z trzech kluczowych komponentów: schemat (Schema), resolvery (Resolvers) i silnik wykonawczy (GraphQL Engine). Schemat określa, jakie typy danych są dostępne, jakie zapytania można wykonywać i jakie argumenty przyjmują. Resolvery to funkcje po stronie serwera, które zwracają dane dla każdego pola schematu. Silnik wykonawczy otrzymuje przychodzące zapytanie, waliduje je względem schematu, wywołuje odpowiednie resolvery i kompiluje odpowiedź.
Proces przetwarzania zapytania wygląda następująco:
Kluczową zaletą architektury GraphQL jest rozwiązywanie na poziomie pól. W REST programista albo otrzymuje wszystkie pola zasobu (być może z nadmiarem), albo ucieka się do rozszerzeń takich jak ?fields=name,email. W GraphQL taka filtracja jest wbudowana w język: każde zapytanie jawnie określa, które pola są potrzebne, a serwer zwraca dokładnie je. Jest to szczególnie ważne dla aplikacji mobilnych, gdzie ilość przesyłanych danych bezpośrednio wpływa na szybkość ładowania i zużycie transferu.
GraphQL definiuje trzy typy operacji, z których każdy odpowiada określonemu scenariuszowi interakcji. Query — do odczytu danych, analogicznie do GET w REST. Mutation — do modyfikacji danych (tworzenie, aktualizacja, usuwanie), analogicznie do POST/PUT/DELETE. Subscription — do aktualizacji w czasie rzeczywistym przez WebSocket, co nie ma bezpośredniego odpowiednika w klasycznym REST (wymaga dodatkowych rozwiązań takich jak WebSocket lub Server-Sent Events).
Podstawowa składnia zapytań jest intuicyjna:
// Proste zapytanie z argumentem
query {
user(id: "42") {
name
email
avatarUrl
}
}
// Mutation ze zwrotem zmodyfikowanych danych
mutation {
updateProfile(name: "Jan") {
id
name
updatedAt
}
}
// Subscription — nasłuchuje aktualizacji w czasie rzeczywistym
subscription {
newMessage(chatId: "chat_1") {
id
text
sender { name }
}
}
Query jest wykonywane równolegle — wszystkie pola na tym samym poziomie są ładowane jednocześnie. Pozwala to załadować powiązane dane (użytkownika i jego posty) jednym zapytaniem bez wielokrotnych round-tripów. Mutation jest wykonywane sekwencyjnie — mutacje w jednym zapytaniu są wykonywane jedna po drugiej w kolejności deklaracji. Subscription ustanawia stałe połączenie przez WebSocket, przez które serwer wysyła dane po wystąpieniu zdarzenia.
Operacje mogą przyjmować zmienne w celu oddzielenia danych od zapytania, dyrektywy (@include, @skip) do warunkowego włączania pól oraz fragmenty do wielokrotnego używania zestawów pól. Te możliwości sprawiają, że zapytania GraphQL są elastyczne i wielokrotnego użytku, co jest szczególnie ważne w dużych projektach z wieloma ekranami i komponentami.
U podstaw GraphQL leży system typów, opisujący wszystkie możliwe dane i operacje API. Schemat (Schema) to opis typów, które serwer może zwrócić, oraz zapytań, które przyjmuje. Schemat jest pisany w języku Schema Definition Language (SDL) i służy jako kontrakt między klientem a serwerem. Klient może uzyskać schemat poprzez introspekcję — specjalne zapytanie __schema, które zwraca pełny opis API.
Przykład schematu dla bloga:
// SDL — Schema Definition Language
type User {
id: ID!
name: String!
email: String
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String
author: User!
}
type Query {
user(id: ID!): User
posts(page: Int): [Post!]!
}
Znak wykrzyknika (!) oznacza pole non-null — będzie ono na pewno obecne w odpowiedzi. Nawiasy kwadratowe [ ] oznaczają listę. GraphQL obsługuje typy skalarne (Int, Float, String, Boolean, ID), typy obiektowe, enum, union, interface i input-typy (dla argumentów mutacji). Ścisłe typowanie samo-dokumentuje API i pozwala narzędziom klienckim generować kod: typy TypeScript, klasy danych Kotlin, struktury Swift.
Introspekcja — unikalna możliwość GraphQL, której brak w REST. Klient może wysłać zapytanie do schematu i otrzymać pełny opis wszystkich typów, pól, argumentów i dyrektyw. Stanowi to podstawę narzędzi takich jak GraphiQL i Apollo Studio, które automatycznie generują dokumentację i autouzupełnianie dla programistów. Introspekcja umożliwia również pisanie automatycznych testów sprawdzających zgodność schematu z oczekiwaną strukturą.
Wybór między GraphQL a REST to jedno z kluczowych pytań architektonicznych przy projektowaniu API. Oba podejścia mają swoje mocne i słabe strony, a wybór zależy od konkretnych wymagań projektu. REST wygrywa prostotą i uniwersalnością, GraphQL — elastycznością i wydajnością zapytań. Przyjrzyjmy się tabeli porównawczej.
| Kryterium | REST | GraphQL |
|---|---|---|
| Struktura odpowiedzi | Stała, serwerowa | Elastyczna, kliencka |
| Overfetching | często — serwer zwraca wszystkie pola | Nie — klient żąda tylko potrzebnych |
| Liczba zapytań | Wielokrotne round-tripy | Jedno zapytanie na wszystkie dane |
| Buforowanie | Natywne buforowanie HTTP | Wymaga ręcznej konfiguracji |
| Typowanie | Nie wbudowane (zależy od formatu) | Ścisłe, poprzez schemat SDL |
| Narzędzia | curl, Postman, Swagger | GraphiQL, Apollo Studio, Introspection |
| Przesyłanie plików | Natywnie przez multipart | Wymaga dodatkowych protokołów |
| Wydajność | Przewidywalna, łatwiej optymalizować | Zależy od złożoności zagnieżdżonych zapytań |
Główną wadą GraphQL jest trudność buforowania. W REST buforowanie HTTP działa na poziomie URL: jedno zapytanie do /api/users/42 zwraca zawsze tę samą strukturę, a odpowiedź można buforować według URL. W GraphQL wszystkie zapytania idą do jednego endpointu, struktura odpowiedzi zależy od treści zapytania. Aby rozwiązać ten problem, Apollo Client używa znormalizowanej pamięci podręcznej po stronie klienta, która dzieli odpowiedzi na osobne encje według id i automatycznie je aktualizuje po otrzymaniu nowych danych.
Kolejnym ważnym aspektem jest problem N+1. Podczas żądania danych zagnieżdżonych (na przykład posty użytkownika i komentarze do każdego postu) GraphQL może wykonać osobne zapytanie SQL dla każdego elementu listy. Rozwiązuje się to za pomocą DataLoader — narzędzia do grupowania i buforowania zapytań do baz danych, które łączy pojedyncze zapytania w jedno zbiorcze. W REST problem ten jest mniej wyraźny, ponieważ programista kontroluje strukturę odpowiedzi po stronie serwera.
Rozpatrzymy praktyczne przykłady użycia GraphQL w aplikacji mobilnej w Kotlin z Apollo Client. Przykłady demonstrują typowe scenariusze: ładowanie danych dla ekranu profilu (query), tworzenie nowego posta (mutation) i subskrypcję nowych komentarzy (subscription). Każdy przykład zawiera zarówno zapytanie GraphQL, jak i kod po stronie klienta.
Jedno zapytanie GraphQL ładuje użytkownika, jego ostatnie posty i całkowitą liczbę obserwujących. W REST wymagałoby to co najmniej 2-3 zapytań: /users/42, /users/42/posts, /users/42/stats. GraphQL łączy je w jeden round-trip, skracając czas ładowania ekranu przy wolnych połączeniach.
// Zapytanie GraphQL (w pliku .graphql)
query ProfileScreen($userId: ID!) {
user(id: $userId) {
name
bio
avatarUrl
posts(limit: 10) {
id
title
createdAt
}
followersCount
followingCount
}
}
// Wywołanie po stronie klienta (Apollo Client + Kotlin)
val response = apolloClient
.query(ProfileScreenQuery(userId = "42"))
.execute()
binding.nameText.text = response.data?.user?.name
Mutacja nie tylko tworzy zasób, ale także zwraca jego aktualne dane do odświeżenia UI. Pole __typename jest używane przez Apollo Client do normalizacji pamięci podręcznej — klient automatycznie aktualizuje rekord Post w cache po pomyślnej odpowiedzi mutacji.
// Mutacja GraphQL
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
createdAt
author {
id
name
}
}
}
// Wywołanie mutacji z typem wejściowym
val input = CreatePostInput(
title = "Nowy post o GraphQL",
content = "GraphQL upraszcza pracę z API..."
)
val result = apolloClient
.mutation(CreatePostMutation(input))
.execute()
Ważną zaletą GraphQL nad REST w kontekście tworzenia aplikacji mobilnych jest automatyczne generowanie kodu. Apollo Client dla Kotlin (Apollo GraphQL) generuje typowo bezpieczne klasy z plików .graphql na etapie kompilacji. Jeśli serwer zmieni schemat, projekt nie zbuduje się do czasu aktualizacji zapytań. Zapobiega to błędom w czasie wykonania, charakterystycznym dla REST, gdzie zmiana struktury odpowiedzi może pozostać niezauważona podczas programowania.
Ekosystem GraphQL obejmuje kilka kluczowych bibliotek i narzędzi, które upraszczają tworzenie i eksploatację. Apollo Client — najpopularniejsza biblioteka kliencka, obsługująca React, iOS, Android i Kotlin Multiplatform. Relay od Facebooka — alternatywa dla aplikacji React z unikalnym podejściem do zarządzania danymi i buforowania. Wybór między Apollo a Relay zależy od platformy i wymagań dotyczących wydajności.
Po stronie serwera dominują Apollo Server (Node.js), Netflix DGS Framework (Kotlin/Java) i graphql-ruby. Do tworzenia schematu i testowania zapytań używane jest GraphiQL — interaktywne IDE wbudowane w przeglądarkę. Apollo Studio dostarcza metryki wydajności, śledzenie zapytań i zarządzanie schematem dla środowiska produkcyjnego. Osobno warto wspomnieć o GraphQL Code Generator — narzędziu generującym typy TypeScript, Kotlin, Swift i Dart ze schematów SDL.
Dla tworzenia aplikacji mobilnych szczególnie interesująca jest Apollo Kotlin (Apollo GraphQL) — biblioteka w pełni napisana w Kotlin z obsługą korutyn, Flow i Multiplatform. Pozwala ona używać tych samych zapytań GraphQL dla Androida i iOS w projektach Kotlin Multiplatform. Apollo Kotlin normalizuje pamięć podręczną, obsługuje błędy na poziomie pól (partial errors) i automatycznie generuje modele danych z plików .graphql. To sprawia, że GraphQL jest preferowanym wyborem dla dużych projektów mobilnych, gdzie liczy się szybkość tworzenia i bezpieczeństwo typów.
Często zadawane pytania
GraphQL nie zastępuje REST, ale oferuje alternatywne podejście. REST lepiej sprawdza się w prostych CRUD-API, buforowaniu przez HTTP i publicznych API z przewidywalnym obciążeniem. GraphQL jest optymalny dla złożonych interfejsów z wieloma powiązanymi danymi.
Migracja jest możliwa stopniowo: GraphQL może działać jako warstwa pośrednia (gateway) przed istniejącymi usługami REST. Wiele firm dodaje GraphQL obok REST, nie wyłączając starego API. Całkowita wymiana wymaga przepisania resolverów.
N+1 występuje, gdy dla każdego elementu listy wykonywane jest osobne zapytanie do bazy danych. Rozwiązuje się go za pomocą DataLoader — biblioteki, która grupuje pojedyncze zapytania w jedno i buforuje wyniki w ramach jednego żądania HTTP.
Specyfikacja GraphQL nie definiuje bezpośrednio przesyłania plików. W praktyce stosuje się: kodowanie base64 (proste, ale nieefektywne dla dużych plików), zapytania multipart zgodnie z protokołem graphql-multipart-request-spec lub osobny endpoint REST dla plików.
Bezpieczeństwo GraphQL wymaga dodatkowych środków: ograniczenie głębokości zagnieżdżenia, limit złożoności zapytania, rate limiting na poziomie operacji. Publiczna introspekcja schematu może ujawnić strukturę danych — w środowisku produkcyjnym zaleca się jej wyłączenie.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również