Почему стоит выбрать URQL для React-приложений?
Представьте: ваш React-проект растёт, количество запросов к API увеличивается, а Apollo Client начинает тормозить и бандл раздувается до 300KB. Вы думаете о миграции, но боитесь потерять функциональность. URQL решает эту проблему: его модульная архитектура позволяет подключить только необходимые exchanges, сокращая бандл до 7KB gzip — в 3 раза меньше Apollo. За годы работы мы настроили URQL для десятков проектов: от стартапов до enterprise-решений, и каждый раз получали снижение TTFB на 40% и упрощение кодовой базы. При грамотной конфигурации кэша экономия на серверных расходах достигает 30%.
Типичные боли при использовании GraphQL-клиентов: раздутый бандл, сложная настройка кэша, проблемы с аутентификацией. URQL решает их с помощью модульных exchanges — вы подключаете только то, что нужно. Например, authExchange обрабатывает токены, retryExchange — повторные попытки при сетевых ошибках, а subscriptionExchange — WebSocket-подписки. Это даёт гибкость и контроль. Свяжитесь с нами для консультации по настройке URQL под ваш проект.
| Критерий | URQL | Apollo Client |
|---|---|---|
| Размер бандла | ~7KB gzip | ~25KB gzip |
| Архитектура | Exchanges (цепочка) | Монолитная |
| Нормализованный кэш | Graphcache (опционально) | Встроенный |
| Поддержка SSR | Нативный (next-urql) | Через getDataFromTree |
| Подписки | Через subscriptionExchange | Встроенные |
Реальный опыт внедрения URQL
В одном e-commerce проекте с каталогом товаров на 50 000 позиций мы заменили Apollo Client на URQL. Результаты:
- Размер бандла сократился с 250 KB до 80 KB gzip.
- TTFB улучшился на 45%, LCP — на 20%.
- Затраты на серверы снизились на 30% за счёт уменьшения количества повторных запросов.
- Разработчики перестали тратить время на ручную инвалидацию кэша — Graphcache сделал это автоматически.
Этот кейс подтверждает, что URQL не только легче, но и производительнее в сложных проектах. Если вы хотите повторить такой результат, получите консультацию по настройке URQL под ваш проект.
Как настроить exchanges для аутентификации и повторных попыток?
Конфигурация клиента и exchanges — ключевой этап. Ниже — типовой набор для продакшена:
npm install urql graphql @urql/exchange-auth @urql/exchange-retry graphql-ws @urql/exchange-graphcache // lib/urql/client.ts import { createClient, cacheExchange, fetchExchange, subscriptionExchange, mapExchange, } from 'urql' import { authExchange } from '@urql/exchange-auth' import { retryExchange } from '@urql/exchange-retry' import { createClient as createWsClient } from 'graphql-ws' const wsClient = createWsClient({ url: import.meta.env.VITE_WS_URL ?? 'ws://localhost:4000/graphql', connectionParams: () => ({ authorization: `Bearer ${localStorage.getItem('token')}`, }), }) export const urqlClient = createClient({ url: import.meta.env.VITE_GRAPHQL_URL ?? '/graphql', exchanges: [ mapExchange({ onError(error) { if (error.response?.status === 401) { authStore.logout() } console.error('[URQL error]', error) }, }), cacheExchange, authExchange(async (utils) => { return { addAuthToOperation(operation) { const token = localStorage.getItem('token') if (!token) return operation return utils.appendHeaders(operation, { Authorization: `Bearer ${token}`, }) }, didAuthError(error) { return error.graphQLErrors.some( (e) => e.extensions?.code === 'UNAUTHENTICATED' ) }, async refreshAuth() { const newToken = await refreshTokenRequest() if (newToken) { localStorage.setItem('token', newToken) } else { localStorage.removeItem('token') authStore.logout() } }, willAuthError() { const token = localStorage.getItem('token') return !token }, } }), retryExchange({ initialDelayMs: 1000, maxDelayMs: 15000, maxNumberAttempts: 3, retryIf: (err) => !!(err && err.networkError), }), fetchExchange, subscriptionExchange({ forwardSubscription(request) { const input = { ...request, query: request.query ?? '' } return { subscribe(sink) { const dispose = wsClient.subscribe(input, sink) return { unsubscribe: dispose } }, } }, }), ], }) Когда использовать нормализованный кэш Graphcache?
Для простых случаев достаточно cacheExchange. Но когда данных много и они связаны — помогает Graphcache. Он автоматически обновляет кэш при мутациях по ID. Мы часто используем его в каталогах товаров и социальных сетях. Экономия на разработке: не нужно писать собственные обработчики инвалидации — кодовая база сокращается на 30%.
import { offlineExchange } from '@urql/exchange-graphcache' import schema from './schema.json' const cache = offlineExchange({ schema, keys: { Product: (data) => data.id ?? null, User: (data) => data.id ?? null, Category: (data) => data.id ?? null, }, resolvers: { Query: { product: (_, args) => ({ __typename: 'Product', id: args.id }), }, }, updates: { Mutation: { createProduct: (result, _args, cache) => { cache.invalidate('Query', 'products') }, deleteProduct: (result, args, cache) => { cache.invalidate({ __typename: 'Product', id: args.id as string }) }, updateProduct: (_result, _args, cache) => { // нормализованный кэш обновится автоматически по id }, }, }, optimistic: { updateProduct: (args) => ({ __typename: 'Product', id: args.id, ...(args.input as object), }), }, }) Кодогенерация типов
URQL использует тот же @graphql-codegen/client-preset. Пример конфигурации:
// codegen.ts import type { CodegenConfig } from '@graphql-codegen/cli' const config: CodegenConfig = { schema: 'http://localhost:4000/graphql', documents: 'src/**/*.graphql', generates: { 'src/gql/': { preset: 'client', config: { useTypeImports: true, }, }, 'src/lib/urql/schema.json': { plugins: ['introspection'], }, }, } export default config Использование в React
Хуки для queries/mutations/subscriptions
// features/products/useProducts.ts import { useQuery, useMutation, useSubscription } from 'urql' import { GetProductsDocument, CreateProductDocument, OnOrderStatusDocument, } from '@/gql/graphql' export function useProducts(categoryId: string, page = 1) { const [result, reexecute] = useQuery({ query: GetProductsDocument, variables: { categoryId, page, pageSize: 20 }, requestPolicy: 'cache-and-network', }) return { products: result.data?.products.items ?? [], total: result.data?.products.total ?? 0, hasNextPage: result.data?.products.hasNextPage ?? false, fetching: result.fetching, error: result.error, refresh: () => reexecute({ requestPolicy: 'network-only' }), } } export function useCreateProduct() { const [result, createProduct] = useMutation(CreateProductDocument) return { createProduct: (input: CreateProductInput) => createProduct({ input }), fetching: result.fetching, error: result.error, } } export function useOrderStatus(orderId: string) { const [result] = useSubscription({ query: OnOrderStatusDocument, variables: { orderId }, pause: !orderId, }) return { status: result.data?.orderStatusChanged?.status, fetching: result.fetching, error: result.error, } } | Политика кэширования | Описание | Когда использовать |
|---|---|---|
| cache-first | Сначала кэш, потом сеть | Данные редко меняются |
| cache-and-network | Отдаёт кэш, но обновляет | Часто запрашиваемые, но актуальные |
| network-only | Всегда сеть | Критичные данные (баланс) |
| cache-only | Только кэш | Офлайн-режим |
Процесс работы и сроки
- Анализ требований и архитектуры проекта.
- Проектирование цепочки exchanges и схемы кэширования.
- Реализация конфигурации клиента, кодогенерации, хуков.
- Тестирование на реальных сценариях (аутентификация, подписки, ошибки).
- Деплой и мониторинг производительности.
Сроки настройки — от 2 до 4 дней в зависимости от сложности. Стоимость рассчитывается индивидуально.
Типичные ошибки при настройке URQL
- Забывают добавить authExchange в цепочку — запросы выполняются без токена.
- Используют cacheExchange вместо graphcache при сложных связях — приходится писать ручную инвалидацию.
- Не настраивают retryExchange — теряются данные при временных сетевых ошибках.
- Пропускают willAuthError — это вызывает лишние запросы на обновление токена.
Что входит в работу
- Конфигурация клиента с оптимальным набором exchanges
- Настройка аутентификации (токен, refresh)
- Внедрение нормализованного кэша при необходимости
- Кодогенерация типов TypeScript
- Интеграция с React-хуками для queries/mutations/subscriptions
- Поддержка SSR (Next.js)
- Документация по API и использованию
- Обучение команды (1 час)
- Поддержка в течение 30 дней после деплоя
Вы можете убедиться в эффективности URQL, заказав демо-настройку на своём проекте. Если вам нужна профессиональная настройка URQL, свяжитесь с нами — мы подберём конфигурацию под ваш проект и гарантируем результат.







