Чому варто обрати 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, зв'яжіться з нами — ми підберемо конфігурацію під ваш проект і гарантуємо результат.







