Skip to content

Сравнение с 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.

Бок о бок

farfetchedeffector-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
декларативный HTTPcreateJsonQuery + createJsonMutationcreateJsonQuery + createJsonMutation (поверх createJsonRequestFx)
пагинацияcreateInfiniteQuery (двунаправленная)
отменаabort + discardреальный AbortSignal через createRequestFx
barrier / мьютексcreateBarrier + оператор applyBarriercreateBarrier + оператор applyBarrier
офлайн-режимсобирается на барьеревстроенный createNetworkBarrier
протокол @@triggerреализует + потребляет (keepFresh triggers)реализует (каждый query/mutation) + потребляет (keepFresh triggers)
роутер@farfetched/atomic-routerattachToRoute (структурно — без импорта роутера)
devtools@farfetched/dev-toolsвизуальные панели (React/Vue/Solid) + поток интроспекции
биндинги@farfetched/solid + useUnitreact / vue / solid + useQuery + useSuspenseQuery
SSRfork / allSettled (in-memory кэш глобален)fork / allSettled + scope-изолированный кэш ($queryCache)
конкурентностьTAKE_LATEST / FIRST / EVERY+ полосы (key) + QUEUE (сериализация)
причины абортовтипизированы на aborted + signal.reason
императивный awaitstartAsync / mutateAsync (настоящие Effects)
runtime-дефолты$queryDefaults (на fork)
debounce / fallbackрукамиинлайн-опции + операторы
оптимистичные апдейтыupdateupdate + 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 берут на себя большую часть механических изменений.

Под лицензией MIT