Представьте помощника с записной книжкой. Вы просите его узнать список постов: он идёт на сервер, приносит ответ и записывает в книжку — не как попало, а по «карточкам». У каждого поста своя карточка Post:1, у каждого автора — User:7. В следующий раз помощник отвечает из книжки, а когда карточка меняется, сам сообщает всем экранам, где она показана. Таков Apollo Client: он ведёт запросы GraphQL, состояния загрузки и ошибок, кеш и обновление интерфейса. В этом уроке вы подключите его к React-клиенту блога, выведете список постов, добавите форму, научитесь обновлять кеш после мутаций и отправлять JWT.
Что даёт GraphQL-клиент
Можно обойтись обычным fetch, как в первом уроке, но придётся вручную вести loading и error, не дублировать запросы и следить, чтобы после мутации все экраны показали новые данные. Клиент берёт это на себя. Сравним с тем, что вы знаете:
- Apollo Client понимает GraphQL и хранит данные нормализованно: по объектам, а не по запросам. Самый популярный выбор.
- TanStack Query (тема про React) кеширует ответы по ключам запросов и подходит для любого API, но объектов GraphQL «не знает».
- urql — лёгкий клиент: по умолчанию кеширует документы целиком, а нормализованный кеш подключается отдельным пакетом.
Подключаем Apollo Client
Создайте проект на Vite и поставьте пакеты. Для Apollo Client 4 нужен ещё rxjs. Адрес сервера лежит в .env: Vite отдаёт браузеру только переменные с префиксом VITE_.
npm create vite@latest blog-web -- --template react-ts
cd blog-web
npm install @apollo/client graphql rxjs
# then create .env with the line: VITE_GRAPHQL_URL=http://localhost:4000/graphqlСсылку на сервер в версии 4 создают явно (new HttpLink), а хуки и ApolloProvider берут из @apollo/client/react. Клиент создают один раз и оборачивают им приложение:
// src/apollo.ts
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';
export const client = new ApolloClient({
link: new HttpLink({ uri: import.meta.env.VITE_GRAPHQL_URL }),
cache: new InMemoryCache(),
});
// src/main.tsx
import { ApolloProvider } from '@apollo/client/react';
import { client } from './apollo.ts';
createRoot(document.getElementById('root')!).render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
);useQuery: список постов
Запрос пишут в шаблонной строке gql. Хук useQuery отправляет его при первом показе компонента и возвращает состояние:
// src/PostList.tsx
import { gql } from '@apollo/client';
import { useQuery } from '@apollo/client/react';
type Post = { id: string; title: string; author: { name: string } };
const POSTS = gql`
query Posts($published: Boolean) {
posts(published: $published) {
id
title
author { id name }
}
}
`;
export function PostList() {
const { data, loading, error, refetch } = useQuery<{ posts: Post[] }>(
POSTS,
{ variables: { published: true } },
);
if (loading && !data) return <p>Loading…</p>;
if (error) return <p>Error: {error.message}</p>;