Graphql jest coraz popularniejszy w aplikacjach. Pozwala na pobieranie dokładnie tych danych, jakich potrzebujemy. W jednym z poprzednich postów pokazałem jak to zaimplementować na backendzie. Dziś przyszedł czas na frontend.
Instalacja i konfiguracja
Najpierw rzeczy oczywiste - instalacja i konfiguracja potrzebnych bibliotek. Będę oczywiście konfigurował projekt napisany w React.
npm i @apollo/client graphql
Do poprawnego działania potrzebujemy tzw.: klienta. Jest to w minimalnej konfiguracji adres serwera graphql, z którego będziemy pobierać dane i rodzaj cache’a. Będziemy z niego korzystać przy każdej operacji.
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:3000/graphql',
cache: new InMemoryCache()
});
Uwaga! URL do Graphql najlepiej przekazywać w zmiennej środowiskowej
Najlepiej jest wyciągnąć konfigurację klienta do osobnego pliku. Dlaczego? Ponieważ będziemy mogli go importować w wielu miejscach. Możemy również mieć kilka klientów w jednej aplikacji.
Dodatkowo w React możemy skorzystać z Contextu, by dostarczyć domyślnego klienta i uprościć pracę.
<ApolloProvider client={client}>
<App />
</ApolloProvider>
Nic nie stoi na przeszkodzie, by osobne części systemu korzystały z różnych Provider’ów. Warto zwrócić uwagę, że możemy przekazać tylko jedną instancję klienta. Powoduje to, że w sytuacji, gdy korzystamy z kilku klientów, może to być nieintuicyjne.
Rodzaje operacji
W GraphQL mamy kilka rodzajów operacji, które możemy wykonać:
- Query - do pobierania danych
- Mutation - do aktualizacji danych
- Subscription - do nasłuchiwania na dane
Do każdej z tych operacji mamy odpowiednie funkcje, które możemy wykorzystać. Dziś skupię się na Query i Mutation
Query
Najprostszą operacją jest pobieranie danych. W GraphQL polega to na wysłaniu odpowiedniego Query. W Apollo Graphql mamy dostępne dwa hooki: useQUery
i useLazyQuery
. Zacznę od tego pierwszego.
const query = gql`
{
links {
url,
name
}
}
`
const {loading, error, data} = useQuery(query, {
fetchPolicy: "network-only",
})
Do wykonania dowolnej operacji potrzebujemy query z Graphql’a. Tworzymy je przy pomocy funkcji gql
. Sam hook wymaga od nas podania tylko tego query do poprawnego działania. Ale oprócz tego możemy przekazać obiekt z dodatkowymi ustawieniami. To z czego ja najcześciej korzystam to:
variables
- do przekazywania zmiennych np.: filtryfetchPolicy
- do ustawienia sposobu, w jaki chce pobrać dane - czy zawsze to ma być zapytanie na backend, czy chcemy wykorzystać cache.
To, co dostajemy to obiekt z odpowiednimi polami. Do tych najczęściej wykorzystywanych można zaliczyć:
loading
- true jeśli zapytanie jest w trakcieerror
- jeśli w trakcie zapytania wystąpił błąd, to tutaj będą informacje o nimdata
- dane zwrócone z backendu
Inne ciekawe pola to:
refetch
- do ponownego uruchomienia query z tymi samymi zmiennymi.fetchMore
- wykorzystywane do paginacji, by pobrać kolejną porcję danych
Ten hook zawsze wykonuje się po zamontowaniu komponentu na stronie. Jeśli chcesz pobrać dane w wyniku jakiejś akcji to musisz wykorzystać LazyQuery
LazyQuery
To jest rozszerzenie poprzedniego hooka. Różni się tylko tym, że zwraca tablicę zamiast obiektu i nie wykonuje się automatycznie. Za to pierwszy element tablicy jest funkcją, która pozwala na pobranie danych. Wygląda to następująco:
const [fetchLinks, {loading, error, data}] = useLazyQuery(query, {
fetchPolicy: "network-only",
})
const fetchLinks = ()=>{
fetchLinks();
}
Reszta elementów jest bez zmian.
Mutation
Na sam koniec zostawiłem mutacje. Jeśli chodzi o strukturę, to wygląda podobnie do LazyQuery. Czyli jako rezultat hooka dostajemy tablicę, gdzie pierwszym elementem jest funkcja, która umożliwia wykonanie mutacji.
const mutation = gql`
mutation AddLink($url: String, $name: String) {
addLink(url: $url, name: $name){
name,
url
}
}
`
const [addLinkMutation, {called, loading, data, error}] = useMutation(mutation);
Tak samo wygląda obiekt z rezultatem wykonania mutacji. Dodatkowym elementem jest flaga called
. A jak przekazać zmienne do mutacji? Bardzo prosto - podczas jej wywoływania.
const addLink = ()=>{
addLinkMutation({variables: {"url":"test", "name":"foo"}});
}
Możemy też tego użyć w inny sposób. Zamiast pobierać dane bezpośrednio z hooka, możemy je pobierać z funkcji mutującej.
const addLink = async ()=>{
const {data} = await addLinkMutation({variables: {"url":"test", "name":"foo"}});
console.log(data);
}
Ciekawym parametrem jaki możemy przekazać w mutacji, obok variables
, jest refetchQueries
. Pozwala na wykonanie query, po tym jak się wykona mutacja. Możesz tego użyć np.: do odświeżenia widoku po tym, jak użytkownik zaktualizował dane. Przykładowy kod wygląda następująco:
const { loading, data } = useQuery(query, {
fetchPolicy: "network-only",
})
const [addLinkMutation] = useMutation(mutation);
const addLink = async () => {
await addLinkMutation({
variables: { "url": "test", "name": "foo" },
refetchQueries: [
{
query: query,
},
],
});
}