Сравнение с farfetched
farfetched — самый полный инструмент для запросов в effector и очевидная точка отсчёта. Он зрелый, хорошо спроектирован и open-source / не заархивирован. Это честное сравнение — включая то, где farfetched всё ещё впереди, — чтобы вы выбрали подходящий инструмент, а не повелись на рекламу.
Разница в одну строку: farfetched моделирует запрос как собственную event-абстракцию (RemoteOperation из handler); effector-refetch оборачивает ваш реальный Effect и даёт дружелюбный inline-конфиг. Разная философия, много пересечений.
Где farfetched всё ещё впереди
Учтите это перед переходом:
- Зрелость и экосистема. Годы в проде, больше сообщество, больше накопленных рецептов и фиксов краевых случаев. effector-refetch пока молодой (0.x).
- Sourced-параметры везде. В farfetched почти любое поле любого оператора может быть
Store/source. effector-refetch делает sourced поля декларативного HTTP (url/query/body/headersвcreateJsonQuery/createJsonMutation) плюс выборочный конфиг —enabled,concurrency,retry.times,cache.staleAfter,refetchInterval,timeout, — а остальное ожидает из параметров эффекта (обычно черезsample). Ближе, чем было, но всё ещё уже. Пара именованных адаптеров валидации.Закрыто: effector-refetch теперь покрывает все именованные адаптеры farfetched —zodContract,runtypesContract,ioTsContract,superstructContract,typedContract— плюсstandardSchemaContract(любая Standard-Schema библиотека — valibot, arktype, zod 4, …),@withease/contracts(работает нативно — та же формаContract, без адаптера) иcreateContract.
Чем effector-refetch отличается (и часто удобнее)
- Effect-first. Единица работы — ваш реальный
Effect(в т.ч.attach-фабрики): виден в devtools, композируется, тестируется отдельно.query.__.effect— ровно то, что вы передали. - Дружелюбный конфиг.
retry/cache/concurrency/timeout— inline-опцииcreateQueryи standalone-операторы (retry(),cache(),concurrency(),timeout(),keepFresh(),applyBarrier()) — сахар над тем же движком. - Реальная отмена.
createRequestFxдаётAbortSignal;TAKE_LATEST/cancelреально прерывают запрос в полёте, а не просто отбрасывают результат. - Декларативный HTTP для чтений и записей.
createJsonQuery+createJsonMutation, оба поверх переиспользуемого request-эффекта (createJsonRequestFx), который можно вставить в любойcreateQuery. @@triggerв обе стороны. Каждый query/mutation является@@trigger(fired=finished.done), поэтому драйвитkeepFresh({ triggers })самого farfetched — а нашkeepFreshв ответ принимает любой@@trigger(web-API триггеры withease, совместимые с farfetched) или обычныйEvent.- Пагинация из коробки.
createInfiniteQuery(двунаправленныеfetchNext/fetchPrevious, окно страниц) — у farfetched встроенного аналога нет. - Офлайн из коробки. У обеих библиотек есть мьютекс
createBarrier(например 401 → обновить токен → повторить); effector-refetch добавляет готовыйcreateNetworkBarrier, который ставит запросы на паузу, пока браузер офлайн. - Роутер структурно.
attachToRoute({ route, query })стартует/сбрасывает запрос на открытии/закрытии маршрута — без импорта atomic-router (подойдёт любой объект{ opened, closed }). - Тулинг. Визуальные devtools-панели для React, Vue и Solid, поток интроспекции,
llms.txtи скилл для Claude Code. - Биндинги и Suspense.
useUnit(query)плюс хелперыuseQueryдля React / Vue / Solid иuseSuspenseQueryдля React Suspense. - Конкурентность всерьёз. Помимо общих TAKE_LATEST/FIRST/EVERY: полосы (
concurrency: { key }— вытеснение в рамках строки без отмены соседей) иQUEUE(строго последовательные прогоны — записи, которым нельзя перемешиваться). - Всегда известно, ПОЧЕМУ прогон закончился.
abortedнесёт типизированную причину ('cancelled' | 'superseded' | 'take-first-busy' | 'disabled'), и она же едет наAbortSignalрана (signal.reason). - Императивно, когда нужно.
await query.startAsync(params)/mutation.mutateAsync(...)— настоящие Effects,useUnitбиндит их scope-корректно. - Runtime-дефолты.
$queryDefaults— переопределиretry/timeout/concurrency/staleAfterнаfork(тесты, SSR), не трогая определения запросов. debounceиfallbackинлайном. Честный до-сетевой debounce (композится с полосами) и восстановление финальной ошибки в данные — без ручной проводки.- Состояние одним объектом.
$state— дискриминированный union:status === 'done'сужаетdataдо non-null,'fail'сужаетerror— рядом с гранулярными сторами. - Оптимистичные апдейты, переживающие рефетчи.
update/optimisticUpdateс параллельными слоями и перебазированием на свежие данные; плюс кросс-модульныйinvalidateTag. - Поэтапная миграция.
effector-refetch/tanstackиeffector-refetch/apolloпрогоняют хэндлер через кэш внешнего клиента на время перехода. - Устойчивость по умолчанию. Бросающие пользовательские колбэки (
mapParams/mapData/…) конвертируются в поток ошибок, а не убивают распространение. - Маленькое ядро без зависимостей (~12 КБ) в активной разработке к 1.0.
Бок о бок
| farfetched | effector-refetch | |
|---|---|---|
| единица работы | внутренний event-исполнитель | ваш реальный Effect — first-class |
| стиль API | операторы | inline-опции и операторы |
| операторы | retry/cache/concurrency/timeout/keepFresh/barrier | тот же набор — inline и standalone |
| sourced-конфиг | sourced всё | поля HTTP (url/query/body/headers) + выборочный конфиг + source/mapParams |
| валидация | runtypes / io-ts / superstruct / typed-contracts / zod | те же пять + Standard Schema + @withease/contracts (нативно) + createContract |
| декларативный HTTP | createJsonQuery + createJsonMutation | createJsonQuery + createJsonMutation (поверх createJsonRequestFx) |
| пагинация | — | createInfiniteQuery (двунаправленная) |
| отмена | abort + discard | реальный AbortSignal через createRequestFx |
| barrier / мьютекс | createBarrier + оператор applyBarrier | createBarrier + оператор applyBarrier |
| офлайн-режим | собирается на барьере | встроенный createNetworkBarrier |
протокол @@trigger | реализует + потребляет (keepFresh triggers) | реализует (каждый query/mutation) + потребляет (keepFresh triggers) |
| роутер | @farfetched/atomic-router | attachToRoute (структурно — без импорта роутера) |
| devtools | @farfetched/dev-tools | визуальные панели (React/Vue/Solid) + поток интроспекции |
| биндинги | @farfetched/solid + useUnit | react / vue / solid + useQuery + useSuspenseQuery |
| SSR | fork / allSettled (in-memory кэш глобален) | fork / allSettled + scope-изолированный кэш ($queryCache) |
| конкурентность | TAKE_LATEST / FIRST / EVERY | + полосы (key) + QUEUE (сериализация) |
| причины абортов | — | типизированы на aborted + signal.reason |
| императивный await | — | startAsync / mutateAsync (настоящие Effects) |
| runtime-дефолты | — | $queryDefaults (на fork) |
| debounce / fallback | руками | инлайн-опции + операторы |
| оптимистичные апдейты | update | update + optimisticUpdate (слои, перебазирование) |
| tag-инвалидация | — | invalidateTag (кросс-модульно) |
| форма состояния | отдельные сторы | гранулярные сторы и union $state |
| interop / миграция | — | effector-refetch/tanstack, effector-refetch/apollo |
| зрелость / экосистема | больше, проверена в бою | молодой, активно развивается |
SSR бок о бок
Обе библиотеки делают SSR по-эффекторовски — fork() на запрос, allSettled, serialize(scope) для состояния сторов ($data / $status восстанавливаются без мигания загрузки). Разница — в слое кэша:
- farfetched резолвит адаптер через внутренний стор
__.$instance(«to support Fork API»), так что подмена инстанса адаптера на форк технически возможна — но по-адаптерно и вручную; дефолтныйinMemoryCacheдержит записи в замыкании, общем для всех scope, а контракт адаптера (get/set/purge/unset) не умеет перечислять записи — встроенного переноса кэша сервер→клиент нет. Ключи неймспейсятся по sid запроса (для кэширования обязателен effector babel/SWC-плагин). - effector-refetch — один переключатель на всё приложение:
fork({ values: [[$queryCache, inMemoryCache()]] })изолирует кэш каждого query в этом scope. Записи неймспейсятся по query (name?? sid эффекта ?? счётчик, плагин не обязателен),dehydrate(cache)/hydrate(cache, snapshot)переносят записи ровно одного запроса с сохранениемstoredAt(staleAfterстареет от серверного фетча), а$queryCacheавтоматически исключён изserialize(scope). См. SSR-рецепт.
Что выбрать?
- Берите farfetched, если нужен самый зрелый вариант сегодня или вы активно полагаетесь на sourced-everything.
- Берите effector-refetch, если предпочитаете оборачивать свои эффекты, хотите inline-конфиг, реальную отмену, встроенную пагинацию, декларативные чтения и записи, примитивы barrier/offline, структурную интеграцию с роутером, кросс-фреймворковые devtools или маленькое ядро на активно поддерживаемом проекте.
Уже на farfetched и интересно? Гайд по миграции и инструмент npx effector-refetch-codemod берут на себя большую часть механических изменений.