10. Apollo Client

Apollo Client — мощный GraphQL клиент для JavaScript. Управляет запросами, кэшированием, состоянием и обновлением UI.
Установка
Заголовок раздела «Установка»npm install @apollo/client graphqlКонфигурация
Заголовок раздела «Конфигурация»import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';import { setContext } from '@apollo/client/link/context';
// HTTP link — подключение к серверуconst httpLink = createHttpLink({ uri: 'http://localhost:4000/graphql',});
// Auth link — добавляем токен в заголовкиconst authLink = setContext((_, { headers }) => { const token = localStorage.getItem('token'); return { headers: { ...headers, authorization: token ? `Bearer ${token}` : '', }, };});
export const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache(), defaultOptions: { watchQuery: { fetchPolicy: 'cache-and-network', // Сначала кэш, потом сеть }, },});import { ApolloProvider } from '@apollo/client';import { client } from './apolloClient';
root.render( <ApolloProvider client={client}> <App /> </ApolloProvider>);InMemoryCache — кэш
Заголовок раздела «InMemoryCache — кэш»Apollo Client автоматически кэширует ответы по нормализованной структуре:
const client = new ApolloClient({ cache: new InMemoryCache({ typePolicies: { User: { // Уникальный ключ для кэша (по умолчанию id) keyFields: ['id'], }, Post: { keyFields: ['id'], fields: { // Кастомная политика слияния для поля comments comments: { merge(existing = [], incoming) { return [...existing, ...incoming]; }, }, }, }, }, }),});Fetch Policies — политики кэширования
Заголовок раздела «Fetch Policies — политики кэширования»useQuery(GET_USERS, { fetchPolicy: 'cache-first', // Из кэша, запрос только если нет fetchPolicy: 'network-only', // Всегда запрос, кэш только для записи fetchPolicy: 'cache-and-network', // Кэш + запрос (обновляет UI дважды) fetchPolicy: 'no-cache', // Без кэша fetchPolicy: 'standby', // Не выполняет запрос nextFetchPolicy: 'cache-first', // После первого запроса});Error Handling на уровне клиента
Заголовок раздела «Error Handling на уровне клиента»import { from, HttpLink } from '@apollo/client';import { onError } from '@apollo/client/link/error';
const errorLink = onError(({ graphQLErrors, networkError }) => { if (graphQLErrors) { graphQLErrors.forEach(({ message, extensions }) => { if (extensions?.code === 'UNAUTHENTICATED') { localStorage.removeItem('token'); window.location.href = '/login'; } console.error(`GraphQL ошибка: ${message}`); }); }
if (networkError) { console.error(`Сетевая ошибка: ${networkError}`); }});
const client = new ApolloClient({ link: from([errorLink, authLink, httpLink]), cache: new InMemoryCache(),});Ручное управление кэшем
Заголовок раздела «Ручное управление кэшем»Обновление после мутации
Заголовок раздела «Обновление после мутации»const [createPost] = useMutation(CREATE_POST, { update(cache, { data: { createPost } }) { // Вариант 1: writeQuery — записываем готовые данные const existing = cache.readQuery({ query: GET_POSTS }); cache.writeQuery({ query: GET_POSTS, data: { posts: { ...existing.posts, items: [createPost, ...existing.posts.items], }, }, });
// Вариант 2: modify — точечное обновление cache.modify({ fields: { posts(existingPosts, { readField }) { const newPostRef = cache.writeFragment({ data: createPost, fragment: gql` fragment NewPost on Post { id title createdAt } `, }); return { ...existingPosts, items: [newPostRef, ...existingPosts.items], total: existingPosts.total + 1, }; }, }, }); },});refetchQueries — перезапрос после мутации
Заголовок раздела «refetchQueries — перезапрос после мутации»const [deletePost] = useMutation(DELETE_POST, { // Простой вариант — указываем query для перезапроса refetchQueries: [{ query: GET_POSTS }], awaitRefetchQueries: true, // Ждём завершения refetch});
// Или динамически:const [deletePost] = useMutation(DELETE_POST, { refetchQueries: (result) => { if (result.data.deletePost.success) { return [{ query: GET_POSTS }]; } return []; },});Apollo Client DevTools
Заголовок раздела «Apollo Client DevTools»Установи расширение Apollo Client Devtools для Chrome:
- Просмотр кэша в реальном времени
- Выполнение запросов прямо из браузера
- Мониторинг всех операций
Чтение/запись в кэш напрямую
Заголовок раздела «Чтение/запись в кэш напрямую»// Читаем из кэшаconst data = client.readQuery({ query: GET_USER, variables: { id: '123' },});
// Пишем в кэшclient.writeQuery({ query: GET_USER, variables: { id: '123' }, data: { user: updatedUser },});
// Читаем фрагмент (конкретный объект)const user = client.readFragment({ id: 'User:123', // Normalized cache key fragment: gql` fragment UserBasic on User { id name email } `,});
// Инвалидируем кэш (принуждаем refetch)client.cache.evict({ id: 'User:123' });client.cache.gc(); // Garbage collectionReactive Variables — локальное состояние
Заголовок раздела «Reactive Variables — локальное состояние»Apollo Client может заменить Redux для простых случаев:
// Объявлениеimport { makeVar } from '@apollo/client';
export const cartItemsVar = makeVar([]);export const isLoggedInVar = makeVar(false);
// Использование в компонентеimport { useReactiveVar } from '@apollo/client';
function CartIcon() { const cartItems = useReactiveVar(cartItemsVar); return <span>🛒 {cartItems.length}</span>;}
function addToCart(item) { cartItemsVar([...cartItemsVar(), item]);}Практика
Заголовок раздела «Практика»- Настрой Apollo Client с authLink для передачи JWT
- Добавь errorLink для редиректа на /login при 401
- Настрой InMemoryCache с keyFields для кастомного ключа
- Обнови кэш после добавления нового поста (без refetch)
- Создай reactive variable для корзины покупок
- Apollo Client = умный кэш + fetch + state management
InMemoryCacheнормализует данные поidfetchPolicyуправляет стратегией кэшированияupdate/refetchQueries— обновление кэша после мутацийmakeVar— реактивные переменные для локального состояния
Следующий урок → React Hooks для GraphQL →