Проект зростає — кількість GraphQL-запитів множиться, а N+1 проблема та розсинхронізація кешу стають головним болем. Ми бачили код, де кожен компонент смикає свій useQuery, а при мутації доводиться вручну скидати кеш. Наприклад, на одному з проєктів інтернет-магазину з 50 000 товарами кожен компонент каталогу виконував свій useQuery, що призводило до N+1 і гальмувань. Після налаштування Apollo Client з InMemoryCache навантаження на API знизилося на 40%.
Apollo Client з нормалізованим InMemoryCache вирішує це: він автоматично оновлює всі компоненти, підписані на змінені сутності. За 5 років ми впровадили його на 15+ проєктах — від інтернет-магазинів до real-time дашбордів. За даними документації Apollo Client, використання нормалізованого кешу скорочує кількість ручних оновлень на 30%.
Нижче — конкретний гайд з налаштування: від конфігурації клієнта до кодогенерації та оптимізації кешу. Без води, лише робочі конфіги та паттерни.
Чому Apollo Client краще за urql для складних проєктів?
| Критерій | Apollo Client | urql | Relay |
|---|---|---|---|
| Нормалізований кеш | Так, InMemoryCache | Так, document cache | Так |
| Підписки | Так | Так | Через subscriptions-transport-ws |
| Розмір (min+gzip) | ~34 KB | ~15 KB | ~45 KB |
| Популярність (npm/week) | 2.5M+ | 500K+ | 400K+ |
| Інтеграція з React | Хуки + HOC | Хуки | Фрагментна модель |
Apollo Client в 5 разів популярніший за urql за завантаженнями npm і надає більш зрілий інструментарій. Для продуктів з десятками взаємозв'язків нормалізований кеш — критична перевага: він економить до 30% часу на розробку, оскільки не потрібно писати оновлення вручну. Крім того, Apollo автоматично об'єднує однакові запити (query deduplication), що знижує навантаження на сервер у високонавантажених застосунках — в urql цієї функції немає.
Як правильно налаштувати Apollo Client?
Встановлення та конфігурація клієнта
Встановіть пакети та створіть клієнт з HTTP і WebSocket links, аутентифікацією та обробкою помилок. Ключовий елемент — InMemoryCache з typePolicies для пагінації:
import { ApolloClient, InMemoryCache, createHttpLink, from, split, ApolloLink } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
import { onError } from '@apollo/client/link/error'
import { GraphQLWsLink } from '@apollo/client/link/subscriptions'
import { createClient as createWsClient } from 'graphql-ws'
import { getMainDefinition } from '@apollo/client/utilities'
const httpLink = createHttpLink({ uri: import.meta.env.VITE_GRAPHQL_URL ?? '/graphql' })
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('token')
return { headers: { ...headers, ...(token ? { authorization: `Bearer ${token}` } : {}) } }
})
const errorLink = onError(({ graphQLErrors, networkError }) => {
graphQLErrors?.forEach(({ message, extensions }) => {
if (extensions?.code === 'UNAUTHENTICATED') { /* logout */ }
})
})
const wsLink = new GraphQLWsLink(createWsClient({ url: import.meta.env.VITE_GRAPHQL_WS_URL, connectionParams: { authorization: `Bearer ${localStorage.getItem('token')}` } }))
const splitLink = split(
({ query }) => getMainDefinition(query).operation === 'subscription',
wsLink,
from([errorLink, authLink, httpLink])
)
export const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
products: {
keyArgs: ['categoryId'],
merge(existing, incoming, { args }) {
return args?.offset ? { ...incoming, items: [...(existing?.items ?? []), ...incoming.items] } : incoming
}
}
}
}
}
}),
defaultOptions: { watchQuery: { fetchPolicy: 'cache-and-network', errorPolicy: 'all' }, query: { fetchPolicy: 'network-only', errorPolicy: 'all' } }
})
Налаштування fragment matcher для фрагментів на інтерфейсах
Якщо схема використовує інтерфейси або union-типи, необхідно налаштувати IntrospectionFragmentMatcher або possibleTypes в кеші. Інакше Apollo не зможе нормалізувати фрагменти, і кеш буде скидатися при кожному запиті. Генеруйте possibleTypes автоматично за допомогою @graphql-codegen/fragment-matcher.
Optimistic updates та оптимістичні мутації
Apollo Client підтримує оптимістичні оновлення: ви можете миттєво оновити UI, поки запит на сервер виконується. Налаштуйте функцію update в useMutation та передайте optimisticResponse. Це знижує perceived latency та покращує UX.
Як прискорити розробку з кодогенерацією?
Кодогенерація типів з GraphQL-схеми економить до 30% часу на написання TypeScript-інтерфейсів. Налаштуйте @graphql-codegen:
npm install -D @graphql-codegen/cli @graphql-codegen/client-preset
npx graphql-codegen init
В codegen.ts вкажіть схему та документи, увімкніть strictScalars:
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
overwrite: true,
schema: 'http://localhost:4000/graphql',
documents: 'src/**/*.graphql',
generates: {
'src/gql/': {
preset: 'client',
config: { useTypeImports: true, strictScalars: true, scalars: { DateTime: 'string', UUID: 'string' } }
}
}
}
export default config
Запускайте npx graphql-codegen --watch в розробці та npx graphql-codegen в CI.
Типові помилки при налаштуванні Apollo Client
Найчастіша — неправильна конфігурація typePolicies. Якщо не задати keyArgs для полів пагінації, кеш буде змішувати дані різних сторінок. Друга помилка — ігнорування errorPolicy: 'all' при обробці часткових помилок. Третя — забувають передати connectionParams для WebSocket при аутентифікації, і підписки падають з 401.
Як налагоджувати проблеми з кешем в Apollo Client?
Використовуйте Apollo DevTools: розширення для Chrome/Firefox візуалізує кеш, історію запитів та мутацій. Воно дозволяє виконувати довільні GraphQL-запити прямо з браузера. DevTools скорочує час дебагу на 40%.
Робота із запитами, мутаціями та підписками
Після кодогенерації використовуйте згенеровані типи та хуки. Приклад документів та хуків:
query GetProducts($categoryId: ID!, $offset: Int, $limit: Int) {
products(categoryId: $categoryId, offset: $offset, limit: $limit) {
items { id name price stock }
total
hasMore
}
}
import { useQuery } from '@apollo/client'
import { GetProductsDocument } from '@/gql/graphql'
export function useProducts(categoryId: string) {
return useQuery(GetProductsDocument, {
variables: { categoryId },
notifyOnNetworkStatusChange: true
})
}
Що входить у роботу?
- Аналіз існуючої GraphQL-схеми та документів
- Конфігурація клієнта з HTTP/WS links, аутентифікацією, error-обробкою
- Налаштування InMemoryCache з typePolicies під ваші сутності
- Кодогенерація TypeScript-типів та хуків (з інтеграцією в CI)
- Інтеграція підписок та аутентифікації
- Тестування та налагодження з Apollo DevTools
- Документація щодо використання та підтримки
Процес роботи та терміни
| Етап | Тривалість |
|---|---|
| Аналіз схеми та документів | 0.5 дня |
| Конфігурація клієнта та кешу | 1 день |
| Кодогенерація та хуки | 1 день |
| Інтеграція підписок та аутентифікації | 0.5 дня |
| Тестування та налагодження | 1 день |
| Документування | 0.5 дня |
Вартість розраховується індивідуально залежно від складності схеми та обсягу робіт. Орієнтовний термін — від 3 до 5 днів.
Гарантуємо: сумісність з будь-яким React-стеком, підтримку останніх версій @apollo/client, детальну документацію. Наш досвід — 15+ проєктів на Apollo Client, включаючи high-load e-commerce. Отримайте консультацію — оцінимо ваш проєкт за 1 день. Замовте налаштування Apollo Client під ваш проєкт — ми зв'яжемося з вами протягом години.







