Safe-fetch 1.0: от библиотеки к экосистеме за 72 часа

от автора

Три дня назад я опубликовал статью про 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 

Что дает адаптер

  1. Автоматический unwrap: { ok: false }throw error

  2. Сохранение типизации: TypeScript знает тип ошибки в React Query

  3. Фабричные функции: createQueryFn, createMutationFn

  4. Умные дефолты: 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:

Компонент

Статус

Назначение

@asouei/safe-fetch

1.0 (stable)

Core функциональность

@asouei/safe-fetch-react-query

0.1 (experimental)

React Query интеграция

@asouei/safe-fetch-swr

Planned soon 2025

SWR интеграция

@asouei/eslint-plugin-safe-fetch

Planned soon 2025

Линтинг правила

Принципы экосистемы

  1. Модульность: каждый адаптер — отдельный пакет

  2. Типобезопасность: TypeScript first во всем

  3. Zero config: работает из коробки с умными дефолтами

  4. Обратная совместимость: апдейты не ломают код

Сравнение с конкурентами

Критерий

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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *