GraphQL w React: Rewolucja w pobieraniu danych
Mateusz Kędziora

Budowanie nowoczesnych aplikacji webowych z użyciem Reacta to standard. Ale sposób pobierania danych, tradycyjnie przez REST API, bywa problematyczny. Tutaj wkracza GraphQL – technologia, która zmienia zasady gry. W tym artykule pokażemy, dlaczego GraphQL zyskuje na popularności w świecie Reacta, jak działa i jakie korzyści przynosi w porównaniu do REST. Zapraszam do lektury!
Wprowadzenie do GraphQL
GraphQL to język zapytań (query language) do API i jednocześnie środowisko uruchomieniowe po stronie serwera, które realizuje te zapytania z wykorzystaniem istniejących danych. Brzmi skomplikowanie? Spokojnie. Najważniejsze jest zrozumienie, że GraphQL daje klientowi (np. aplikacji React) pełną kontrolę nad tym, jakie dane chce otrzymać z serwera. Koniec z pobieraniem “na zapas” i marnowaniem zasobów!
Zanim przejdziemy dalej, porównajmy to z typowym podejściem REST.
REST – Status Quo
W architekturze REST komunikacja opiera się na zasobach (np. użytkownik, produkt) identyfikowanych przez adresy URL (endpointy). Aplikacja kliencka wysyła żądanie HTTP do konkretnego endpointu, a serwer zwraca reprezentację tego zasobu (najczęściej w formacie JSON).
Problem z REST? Często endpoint zwraca więcej danych, niż aplikacja potrzebuje (tzw. over-fetching), albo żeby zdobyć wszystkie potrzebne informacje, trzeba wykonać kilka oddzielnych żądań (tzw. under-fetching).
Przykład REST
Załóżmy, że mamy aplikację wyświetlającą listę książek i chcemy pokazać tytuł, autora i liczbę stron każdej książki. W REST API moglibyśmy mieć endpoint /books
, który zwraca:
[
{
"id": 1,
"title": "Władca Pierścieni",
"author": "J.R.R. Tolkien",
"pages": 1178,
"genre": "Fantasy",
"publication_date": "1954-07-29"
},
{
"id": 2,
"title": "Hobbit",
"author": "J.R.R. Tolkien",
"pages": 310,
"genre": "Fantasy",
"publication_date": "1937-09-21"
}
]
Jeżeli potrzebujemy tylko tytułu, autora i liczby stron, pobieramy dużo zbędnych informacji (genre, publication_date). To przykład over-fetchingu.
Przykład GraphQL
W GraphQL zapytanie wyglądałoby tak:
query {
books {
title
author
pages
}
}
A serwer zwróci tylko to, o co poprosiliśmy:
{
"data": {
"books": [
{
"title": "Władca Pierścieni",
"author": "J.R.R. Tolkien",
"pages": 1178
},
{
"title": "Hobbit",
"author": "J.R.R. Tolkien",
"pages": 310
}
]
}
}
Dokładnie to, czego potrzebujemy! To esencja GraphQL.
Jak Działa GraphQL?
GraphQL opiera się na kilku kluczowych koncepcjach:
- Schema (Schemat): Definicja typów danych i relacji między nimi. To “kontrakt” między klientem a serwerem. Schemat określa, jakie zapytania są możliwe i jakie dane można pobrać.
- Types (Typy): Reprezentują strukturę danych, np.
Book
,Author
. Typy mogą zawierać pola (fields) o określonych typach danych (np.String
,Int
,Boolean
). - Query (Zapytanie): Prośba o dane. Klient wysyła zapytanie GraphQL do serwera, określając dokładnie, jakich danych potrzebuje.
- Mutation (Mutacja): Służy do modyfikacji danych (np. dodawanie, aktualizacja, usuwanie).
- Resolver (Rozwiązywacz): Funkcja po stronie serwera, która pobiera dane dla konkretnego pola w zapytaniu. Resolver “rozwiązuje” żądanie danych.
Przykład Schematu GraphQL
Oto przykład prostego schematu GraphQL dla biblioteki książek:
type Book {
id: ID!
title: String!
author: Author!
pages: Int
}
type Author {
id: ID!
name: String!
books: [Book]
}
type Query {
book(id: ID!): Book
books: [Book]
author(id: ID!): Author
authors: [Author]
}
Wyjaśnienie:
type Book
: Definiuje typBook
z polamiid
,title
,author
ipages
. Wykrzyknik (!
) oznacza, że pole jest wymagane (nie może być null).type Author
: Definiuje typAuthor
z polamiid
,name
ibooks
. Polebooks
jest listą typuBook
.type Query
: Definiuje punkty wejścia do zapytań. Możemy pobrać pojedynczą książkę po jejid
(book(id: ID!): Book
), listę wszystkich książek (books: [Book]
), autora po jegoid
(author(id: ID!): Author
) lub listę wszystkich autorów (authors: [Author]
).
Przykład Resolvera
Resolver dla zapytania books
mógłby wyglądać tak (przy użyciu Node.js i Express):
const books = [
{ id: '1', title: 'Władca Pierścieni', authorId: '1', pages: 1178 },
{ id: '2', title: 'Hobbit', authorId: '1', pages: 310 },
{ id: '3', title: 'Harry Potter', authorId: '2', pages: 600 }
];
const authors = [
{ id: '1', name: 'J.R.R. Tolkien' },
{ id: '2', name: 'J.K. Rowling' }
];
const resolvers = {
Query: {
books: () => books,
book: (parent, args) => books.find(book => book.id === args.id),
authors: () => authors,
author: (parent, args) => authors.find(author => author.id === args.id)
},
Book: {
author: (parent, args) => authors.find(author => author.id === parent.authorId)
},
Author: {
books: (parent, args) => books.filter(book => book.authorId === parent.id)
}
};
module.exports = resolvers;
Wyjaśnienie:
Query
: Zawiera resolvry dla zapytań zdefiniowanych w schemacie.books: () => books
: Resolver dla zapytaniabooks
zwraca po prostu całą tablicębooks
.book: (parent, args) => books.find(book => book.id === args.id)
: Resolver dla zapytaniabook
pobiera argumentid
z zapytania i szuka książki o tymid
w tablicybooks
.Book: { author: ... }
: Resolver dla polaauthor
w typieBook
. PobieraauthorId
z obiektuBook
i szuka autora o tymid
w tablicyauthors
.Author: { books: ... }
: Resolver dla polabooks
w typieAuthor
. Pobieraid
autora i filtruje listę książek, aby zwrócić tylko te, którychauthorId
odpowiadaid
autora.
GraphQL w React: Jak to Działa?
Ok, mamy podstawy GraphQL. Teraz zobaczmy, jak to działa w praktyce z Reactem. Potrzebujemy biblioteki, która ułatwi nam komunikację z serwerem GraphQL. Najpopularniejsze opcje to:
- Apollo Client: Bardzo rozbudowana biblioteka z wieloma funkcjonalnościami, takimi jak zarządzanie stanem, cache, optymistyczne aktualizacje.
- Relay: Framework od Facebooka, zaprojektowany specjalnie do pracy z GraphQL. Bardziej skomplikowany niż Apollo, ale oferuje zaawansowane funkcje optymalizacji.
- urql: Lekka i elastyczna biblioteka, łatwa do integracji z Reactem.
W tym artykule skupimy się na Apollo Client, ze względu na jego popularność i bogate możliwości.
Instalacja Apollo Client
npm install @apollo/client graphql
Konfiguracja Apollo Client
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:4000/graphql', // Adres Twojego serwera GraphQL
cache: new InMemoryCache()
});
function App() {
return (
<ApolloProvider client={client}>
{/* Twoja aplikacja React */}
</ApolloProvider>
);
}
export default App;
Wyjaśnienie:
ApolloClient
: Tworzy instancję klienta Apollo.uri
: Adres URL serwera GraphQL.cache
: Konfiguruje cache w pamięci, co przyspiesza pobieranie danych (nie trzeba za każdym razem wysyłać zapytania do serwera).ApolloProvider
: Komponent Reacta, który udostępnia instancjęApolloClient
wszystkim komponentom potomnym.
Wykonywanie Zapytań GraphQL w React
Do wykonywania zapytań GraphQL w komponentach React używamy hooka useQuery
.
import { useQuery, gql } from '@apollo/client';
const GET_BOOKS = gql`
query GetBooks {
books {
id
title
author {
name
}
pages
}
}
`;
function BookList() {
const { loading, error, data } = useQuery(GET_BOOKS);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error : {error.message}</p>;
return (
<ul>
{data.books.map(book => (
<li key={book.id}>
{book.title} by {book.author.name} ({book.pages} pages)
</li>
))}
</ul>
);
}
export default BookList;
Wyjaśnienie:
gql
: Funkcja z biblioteki@apollo/client
, która parsuje ciąg znaków zawierający zapytanie GraphQL i tworzy obiekt typuDocumentNode
.GET_BOOKS
: Zapytanie GraphQL pobierające listę książek z serwera. Określamy, jakie pola chcemy pobrać dla każdej książki (id, tytuł, autor (tylko imię), liczba stron).useQuery(GET_BOOKS)
: Hook Apollo Clienta, który wykonuje zapytanieGET_BOOKS
. Zwraca obiekt z właściwościamiloading
,error
idata
.loading
:true
, jeśli zapytanie jest w trakcie wykonywania.error
: Obiekt błędu, jeśli wystąpił błąd podczas wykonywania zapytania.data
: Dane zwrócone przez serwer GraphQL.- Renderowanie: Jeśli dane zostały pobrane (brak
loading
ierror
), renderujemy listę książek, pobierając informacje z obiektudata
.
Wykonywanie Mutacji GraphQL w React
Do wykonywania mutacji (zmiany danych) używamy hooka useMutation
.
import { useMutation, gql } from '@apollo/client';
const ADD_BOOK = gql`
mutation AddBook($title: String!, $authorId: String!, $pages: Int) {
addBook(title: $title, authorId: $authorId, pages: $pages) {
id
title
author {
name
}
pages
}
}
`;
function AddBookForm() {
const [addBook, { loading, error }] = useMutation(ADD_BOOK);
const handleSubmit = (event) => {
event.preventDefault();
const title = event.target.title.value;
const authorId = event.target.authorId.value;
const pages = parseInt(event.target.pages.value);
addBook({ variables: { title, authorId, pages } });
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error : {error.message}</p>;
return (
<form onSubmit={handleSubmit}>
<label>
Title:
<input type="text" name="title" />
</label>
<label>
Author ID:
<input type="text" name="authorId" />
</label>
<label>
Pages:
<input type="number" name="pages" />
</label>
<button type="submit">Add Book</button>
</form>
);
}
export default AddBookForm;
Wyjaśnienie:
ADD_BOOK
: Mutacja GraphQL dodająca nową książkę. Określamy, jakie pola chcemy otrzymać w odpowiedzi po dodaniu książki (id, tytuł, autor (tylko imię), liczba stron). Definiujemy również argumenty mutacji ($title
,$authorId
,$pages
).useMutation(ADD_BOOK)
: Hook Apollo Clienta, który przygotowuje mutacjęADD_BOOK
. Zwraca funkcjęaddBook
(do wywołania mutacji) oraz obiekt z właściwościamiloading
ierror
.handleSubmit
: Funkcja obsługująca wysłanie formularza. Pobiera wartości z pól formularza i wywołuje funkcjęaddBook
z obiektemvariables
zawierającym wartości argumentów mutacji.- Wywołanie
addBook({ variables: { title, authorId, pages } })
wysyła mutację na serwer.
Korzyści z Używania GraphQL w React
- Precyzyjne Pobieranie Danych: Pobierasz tylko to, czego potrzebujesz, eliminując over-fetching.
- Unikanie Wielu Żądań: Pobierasz wszystkie potrzebne dane w jednym zapytaniu, eliminując under-fetching.
- Silne Typowanie: Schemat GraphQL definiuje strukturę danych, co pomaga unikać błędów i ułatwia debugowanie.
- Automatyczna Dokumentacja: Narzędzia takie jak GraphiQL umożliwiają eksplorację API i automatyczne generowanie dokumentacji.
- Lepsza Wydajność: Mniejsze rozmiary odpowiedzi i mniejsza liczba żądań przekładają się na lepszą wydajność aplikacji.
- Elastyczność: GraphQL daje klientowi większą kontrolę nad danymi, co ułatwia dostosowywanie aplikacji do różnych potrzeb.
- Łatwiejsze Wprowadzanie Zmian: Zmiany w backendzie rzadziej wymagają zmian w frontendzie, ponieważ frontend samodzielnie wybiera, jakie dane pobiera.
Porównanie GraphQL z REST
Cecha | REST | GraphQL |
---|---|---|
Pobieranie danych | Endpointy zwracają stałe struktury danych | Klient określa, jakie dane chce pobrać |
Over-fetching | Częste | Brak |
Under-fetching | Częste | Brak |
Liczba żądań | Może wymagać wielu żądań | Zazwyczaj jedno żądanie |
Typowanie | Opcjonalne | Wymagane |
Dokumentacja | Wymaga oddzielnego narzędzia | Automatyczna |
Elastyczność | Ograniczona | Wysoka |
Najlepsze Praktyki GraphQL w React
Używaj Fragmentów: Fragmenty pozwalają na ponowne wykorzystanie fragmentów zapytań w różnych komponentach.
fragment BookFields on Book { id title author { name } } query GetBooks { books { ...BookFields } }
Zarządzaj Stanem Lokalnym: Wykorzystaj możliwości Apollo Clienta do zarządzania stanem lokalnym aplikacji, np. do przechowywania danych użytkownika, filtrów.
Optymistyczne Aktualizacje: Stosuj optymistyczne aktualizacje, aby poprawić responsywność aplikacji. Aplikacja natychmiast aktualizuje UI, zakładając, że mutacja zakończy się sukcesem. Jeśli mutacja zakończy się błędem, UI jest cofane do poprzedniego stanu.
Paginacja: Implementuj paginację na serwerze i po stronie klienta, aby efektywnie obsługiwać duże zbiory danych.
Używaj Narzędzi Developerskich: Korzystaj z narzędzi developerskich Apollo Clienta, aby analizować zapytania, mutacje i cache.
Podsumowanie
GraphQL w połączeniu z Reactem to potężne narzędzie do budowania wydajnych i elastycznych aplikacji webowych. Dzięki precyzyjnemu pobieraniu danych, unikaniu wielu żądań i silnemu typowaniu, GraphQL pozwala na tworzenie aplikacji o lepszej wydajności i mniejszej złożoności. Apollo Client ułatwia integrację GraphQL z Reactem, oferując bogate możliwości zarządzania stanem, cache i optymistycznych aktualizacji.
Zachęcam do samodzielnego eksperymentowania z GraphQL i Apollo Clientem. Stwórz prostą aplikację React pobierającą dane z serwera GraphQL i przekonaj się na własne oczy o korzyściach płynących z tej technologii.
Przydatne Linki
- Oficjalna Dokumentacja GraphQL: https://graphql.org/
- Oficjalna Dokumentacja Apollo Client: https://www.apollographql.com/docs/react/
- GraphQL Tutorial: https://graphql.org/learn/
- Apollo Client Tutorial: https://www.apollographql.com/tutorials/
- GraphQL.org – Learning: https://graphql.org/learn/
- How to GraphQL: https://www.howtographql.com/
- GitHub: Apollo Client: https://github.com/apollographql/apollo-client
Powodzenia w Twojej przygodzie z GraphQL i Reactem! Mam nadzieję, że ten artykuł był pomocny. Zaglądaj na naszego bloga po więcej artykułów o nowoczesnych technologiach webowych.
Polecane artykuły
Docker vs Kubernetes: Który dla Ciebie w 2025?
Docker i Kubernetes objaśnione! Która technologia lepsza dla początkujących w 2025? Porównanie, przykłady i przyszłość.
Mateusz Kędziora
DevOps: Automatyzacja zadań sysadmina dla programistów
Zautomatyzuj pracę sysadmina w środowisku DevOps! Praktyczne przykłady, skrypty, Ansible, Terraform, Prometheus i Grafana.
Mateusz Kędziora
Automatyzacja Linux/macOS z Bash: Praktyczny Przewodnik
Zacznij automatyzować system Linux/macOS z Bash! Dowiedz się, czym jest Bash, jak pisać skrypty i używać podstawowych komend.
Mateusz Kędziora