Три дня назад я опубликовал статью про safe-fetch — библиотеку, которая убирает try/catch из HTTP-запросов. Вчера статья набрала 8,5K просмотров и 64 добавления в закладки. А сегодня представляю safe-fetch 1.0 — уже не просто библиотеку, а целую экосистему.
История о том, как фидбек сообщества за 72 часа превратил обертку над fetch в полноценную платформу для типобезопасного HTTP.
Как всё началось
1 сентября выпустил safe-fetch 0.1.0 и написал статью. Идея простая: вместо try/catch везде — безопасные результаты с типизированными ошибками.
// Было try { const res = await fetch('/users'); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); } catch (e) { console.error('Что-то сломалось:', e.message); } // Стало const result = await safeFetch.get<User[]>('/users'); if (result.ok) { console.log(result.data); } else { console.error(result.error.name); // NetworkError | TimeoutError | HttpError }
4 сентября статья вышла на Хабре. 5 сентября — сегодня — выпускаю 1.0 с экосистемой.
Что произошло за три дня
Статистика взорвалась
-
8,5K просмотров за первые сутки
-
64 добавления в закладки
-
30 звезд на GitHub
-
70 скачиваний с NPM
-
2 пулл-реквеста от сообщества
-
Попадание в Awesome TypeScript
Фидбек показал направление
Комментарии были конкретными:
-
«Как интегрировать с React Query?»
-
«А для SWR будет?»
-
«Можно ли это организовать как монорепо?»
-
«Нужен ESLint plugin для проверки { ok }»
Стало понятно: одной библиотеки недостаточно. Нужна экосистема.
Safe-fetch 1.0: стабилизация
Первое изменение — переход на версию 1.0.0. Не новые фичи, а стабилизация архитектуры:
Монорепо структура
Переезд в workspace для управления несколькими пакетами:
packages/ ├── core/ # @asouei/safe-fetch └── react-query/ # @asouei/safe-fetch-react-query
Современный стек
-
Vite + Vitest вместо custom build
-
GitHub Actions для всех пакетов сразу
-
ESM/CJS экспорты улучшены
Zero breaking changes
Весь существующий код работает без изменений:
const result = await safeFetch.get('/api/data'); // Точно так же, как в 0.1.0
Новинка: React Query адаптер
Самый частый вопрос касался React Query. Проблема понятна: React Query ожидает throws, safe-fetch возвращает { ok: false }.
Проблема в деталях
В React Query принято бросать ошибки для их обработки:
const { data, error } = useQuery({ queryFn: async () => { const response = await fetch('/users'); if (!response.ok) throw new Error('Failed'); return response.json(); } });
С safe-fetch приходилось делать unwrap вручную:
const { data, error } = useQuery({ queryFn: async () => { const result = await safeFetch.get<User[]>('/users'); if (!result.ok) throw result.error; // каждый раз вручную return result.data; }, retry: false // не забыть отключить });
Решение: адаптер
Встречайте @asouei/safe-fetch-react-query@0.1.0:
npm install @asouei/safe-fetch-react-query
import { createQueryFn, rqDefaults } from '@asouei/safe-fetch-react-query'; const queryFn = createQueryFn(api); const { data, error } = useQuery({ queryKey: ['users'], queryFn: queryFn<User[]>('/users'), ...rqDefaults() // { retry: false } + другие настройки }); // error теперь typed: NetworkError | HttpError | TimeoutError | ValidationError
Что дает адаптер
-
Автоматический unwrap:
{ ok: false }→throw error -
Сохранение типизации: TypeScript знает тип ошибки в React Query
-
Фабричные функции:
createQueryFn,createMutationFn -
Умные дефолты:
rqDefaults()отключает дублирующие ретраи
Сравнение до и после
Раньше: boilerplate в каждом хуке
function useUsers() { return useQuery({ queryKey: ['users'], queryFn: async () => { const result = await safeFetch.get<User[]>('/users'); if (!result.ok) throw result.error; // копипаста return result.data; }, retry: false // можно забыть }); } function useCreateUser() { return useMutation({ mutationFn: async (data: NewUser) => { const result = await safeFetch.post<User>('/users', data); if (!result.ok) throw result.error; // опять return result.data; } }); }
Теперь: фабрики решают все
const api = createSafeFetch({ baseURL: '/api' }); const queryFn = createQueryFn(api); const mutationFn = createMutationFn(api); function useUsers() { return useQuery({ queryKey: ['users'], queryFn: queryFn<User[]>('/users'), ...rqDefaults() }); } function useCreateUser() { return useMutation({ mutationFn: mutationFn<User>('/users', { method: 'POST' }) }); }
Результат: 45 строк unwrap кода → 0 строк. Время рефакторинга: 20 минут.
История из production
Тестировал адаптер на реальном проекте. Было 15 хуков с React Query, каждый с одинаковым unwrap паттерном.
Проблемы до адаптера:
-
8 раз забыл
retry: false— получал двойные ретраи -
3 раза TypeScript не поймал ошибку типизации
-
45 строк дублирующегося кода
После внедрения:
-
0 забытых настроек —
rqDefaults()всегда одинаковые -
Полная типизация ошибок работает из коробки
-
Вся логика в фабриках — единая точка истины
Что значит экосистема
Safe-fetch больше не просто библиотека. Это платформа для типобезопасного HTTP:
Принципы экосистемы
-
Модульность: каждый адаптер — отдельный пакет
-
Типобезопасность: TypeScript first во всем
-
Zero config: работает из коробки с умными дефолтами
-
Обратная совместимость: апдейты не ломают код
Сравнение с конкурентами
|
Критерий |
safe-fetch ecosystem |
axios + react-query |
ky + react-query |
|---|---|---|---|
|
Безопасные результаты |
✅ Встроено |
❌ Исключения |
❌ Исключения |
|
Типизированные ошибки |
✅ В React Query тоже |
❌ Нет |
❌ Нет |
|
Общий таймаут операции |
✅ totalTimeoutMs |
❌ Только на запрос |
❌ Только на запрос |
|
Retry-After поддержка |
✅ Автоматически |
❌ Вручную |
❌ Вручную |
|
Unwrap boilerplate |
✅ Адаптер решает |
❌ В каждом хуке |
❌ В каждом хуке |
|
Bundle size |
~3kb + ~0.5kb |
~13kb |
~11kb |
Quick start с экосистемой
Установка
npm install @asouei/safe-fetch @asouei/safe-fetch-react-query
Настройка API
import { createSafeFetch } from '@asouei/safe-fetch'; import { createQueryFn, createMutationFn, rqDefaults } from '@asouei/safe-fetch-react-query'; const api = createSafeFetch({ baseURL: '/api', timeoutMs: 5000, retries: { retries: 2 } }); const queryFn = createQueryFn(api); const mutationFn = createMutationFn(api);
Использование в компонентах
function UserProfile({ userId }: { userId: string }) { const { data: user, error, isLoading } = useQuery({ queryKey: ['user', userId], queryFn: queryFn<User>(`/users/${userId}`), ...rqDefaults() }); const updateUser = useMutation({ mutationFn: mutationFn<User>(`/users/${userId}`, { method: 'PUT' }) }); if (isLoading) return <div>Loading...</div>; if (error) { // error typed as NetworkError | HttpError | TimeoutError | ValidationError return <div>Error: {error.name} - {error.message}</div>; } return ( <div> <h1>{user?.name}</h1> <button onClick={() => updateUser.mutate({ name: 'New Name' })}> Update </button> </div> ); }
Roadmap экосистемы
Stage 1 2025: расширение интеграций
-
SWR адаптер — аналогично React Query (0.x experimental)
-
Next.js стартер — готовый шаблон проекта
-
Cloudflare Workers примеры — для edge computing
Stage 2 2025: dev tools
-
ESLint plugin — правила для паттерна
{ ok } -
VS Code extension — сниппеты и автодополнение
-
Chrome DevTools — инспектирование safe-fetch запросов
Stage 3 2025: продвинутые фичи
-
OpenAPI генератор — typed клиенты из схем
-
GraphQL мост — safe results для GraphQL
-
Request deduplication — автоматическая дедупликация запросов
Отзывы сообщества
За первый день собрался интересный фидбек:
«Надо будет «потыкать», так как хочется предложить альтернативу axios’у в проектах.» — техлид из e-commerce
«Попробовал на pet проекте. Больше не хочу возвращаться к обычному fetch» — фронтенд разработчик
Для кого эта экосистема
Команды на React Query + TypeScript
Если уже используете TanStack React Query и цените типизацию — адаптер решает все проблемы интеграции.
Проекты с жесткими SLA
Комбинация safe-fetch (надежные таймауты + ретраи) + React Query (кеширование) = bulletproof архитектура.
Растущие команды
Фабричный подход унифицирует HTTP в команде. Джуниоры физически не смогут написать неправильный код.
Заключение: от идеи до экосистемы
За 72 часа произошла эволюция:
-
1 сентября: идея и релиз 0.1.0
-
4 сентября: статья на Хабре, фидбек сообщества
-
5 сентября: экосистема 1.0 с React Query адаптером
Safe-fetch core 1.0 стабилен и готов к production. React Query adapter 0.1 — экспериментальный, но уже снимает весь unwrap-бойлерплейт. Попробуйте, киньте фидбек в Issues и поставьте звезду на GitHub — это реально помогает проекту расти.
Следующая статья будет про SWR адаптер. Подписывайтесь на эволюцию экосистемы в реальном времени.
Ссылки:
ссылка на оригинал статьи https://habr.com/ru/articles/944362/
Добавить комментарий