English version | Русская версия
Never write
try/catch
for 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.
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 |
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}`);
}
- 🛡️ 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-After
support - 📦 Zero Dependencies: Tree-shakable, ~3kb, works everywhere
- 🧪 Validation Ready: Built-in Zod integration without exceptions
- Core Library - Complete API documentation, examples, and migration guides
- React Query Adapter - TanStack Query integration
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 }
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 }
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');
- ✅ 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
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
});
}
// 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} />;
}
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 }
);
}
};
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
MIT © Aleksandr Mikhailishin
Built with ❤️ for developers who value predictable, type-safe HTTP clients.