@asouei/safe-fetch
September 5, 2025 · View on GitHub
English version | Русская версия
Never write
try/catchfor HTTP requests again. A complete ecosystem of type-safe HTTP utilities built around safe results and predictable error handling.
Modern HTTP client ecosystem that eliminates exceptions through discriminated unions, provides intelligent retries, handles timeouts properly, and integrates seamlessly with popular data fetching libraries.
📦 Packages
| Package | Version | Description |
|---|---|---|
| @asouei/safe-fetch | Core HTTP client with safe results, retries, and timeouts | |
| @asouei/safe-fetch-react-query | TanStack Query integration with optimized error handling |
🚀 Quick Start
npm install @asouei/safe-fetch
import { safeFetch } from '@asouei/safe-fetch';
const result = await safeFetch.get<{ users: User[] }>('/api/users');
if (result.ok) {
// TypeScript knows result.data is { users: User[] }
console.log(result.data.users);
} else {
// All errors are normalized and typed
console.error(`${result.error.name}: ${result.error.message}`);
}
✨ Why safe-fetch?
- 🛡️ No Exceptions: Never write
try/catch— always get a safe result - 🔧 Typed Errors:
NetworkError | TimeoutError | HttpError | ValidationError - ⏱️ Smart Timeouts: Per-attempt + total operation timeouts
- 🔄 Intelligent Retries: Only retries safe operations +
Retry-Aftersupport - 📦 Zero Dependencies: Tree-shakable, ~3kb, works everywhere
- 🧪 Validation Ready: Built-in Zod integration without exceptions
📖 Documentation
- Core Library - Complete API documentation, examples, and migration guides
- React Query Adapter - TanStack Query integration
🌟 Core Features
Safe Results
Every request returns a discriminated union - no more guessing what went wrong:
type SafeResult<T> =
| { ok: true; data: T; response: Response }
| { ok: false; error: NormalizedError; response?: Response }
Normalized Errors
All errors follow the same structure:
// Network issues, connection failures
type NetworkError = { name: 'NetworkError'; message: string; cause?: unknown }
// Request timeouts (per-attempt or total)
type TimeoutError = { name: 'TimeoutError'; message: string; timeoutMs: number }
// HTTP 4xx/5xx responses
type HttpError = { name: 'HttpError'; message: string; status: number; body?: unknown }
// Schema validation failures
type ValidationError = { name: 'ValidationError'; message: string; cause?: unknown }
Smart Configuration
import { createSafeFetch } from '@asouei/safe-fetch';
const api = createSafeFetch({
baseURL: 'https://api.example.com',
timeoutMs: 5000, // Per attempt
totalTimeoutMs: 30000, // Total operation
retries: {
retries: 2,
baseDelayMs: 300 // Exponential backoff
},
headers: { Authorization: 'Bearer token' }
});
const result = await api.get<User[]>('/users');
🔮 Ecosystem Roadmap
- ✅ Core Library - Safe HTTP client with retries and timeouts
- ✅ React Query Adapter - Optimized TanStack Query integration
- 📋 SWR Adapter - SWR integration helpers
- 🔍 ESLint Plugin - Enforce safe result patterns
- 🏗️ Framework Examples - Next.js, Remix, Cloudflare Workers
📱 Framework Integration
React Query
import { createSafeFetch } from '@asouei/safe-fetch';
import { createQueryFn, rqDefaults } from '@asouei/safe-fetch-react-query';
const api = createSafeFetch({ baseURL: '/api' });
const queryFn = createQueryFn(api);
export function useUsers() {
return useQuery({
queryKey: ['users'],
queryFn: queryFn<User[]>('/users'),
...rqDefaults() // { retry: false } - let safe-fetch handle retries
});
}
Next.js / SSR
// app/users/page.tsx
import { safeFetch } from '@asouei/safe-fetch';
export default async function UsersPage() {
const result = await safeFetch.get<User[]>('/api/users');
if (!result.ok) {
return <ErrorPage error={result.error} />;
}
return <UserList users={result.data} />;
}
Cloudflare Workers
export default {
async fetch(request: Request) {
const result = await safeFetch.get<{ status: string }>('https://api.service.com/health');
return new Response(
result.ok ? JSON.stringify(result.data) : result.error.message,
{ status: result.ok ? 200 : 500 }
);
}
};
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
Quick development setup:
git clone https://github.com/asouei/safe-fetch.git
cd safe-fetch
pnpm install
pnpm -r test
pnpm -r build
📄 License
MIT © Aleksandr Mikhailishin
Built with ❤️ for developers who value predictable, type-safe HTTP clients.