{"id":30804870,"url":"https://github.com/asouei/safe-fetch","last_synced_at":"2025-09-08T05:03:00.017Z","repository":{"id":312684140,"uuid":"1048344473","full_name":"Asouei/safe-fetch","owner":"Asouei","description":"Tiny, type-safe fetch wrapper: safe results, retries, timeouts \u0026 validation","archived":false,"fork":false,"pushed_at":"2025-09-05T08:30:49.000Z","size":134,"stargazers_count":55,"open_issues_count":1,"forks_count":2,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-08T05:02:20.988Z","etag":null,"topics":["error-handling","fetch","http-client","npm-package","retry","timeout","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@asouei/safe-fetch","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Asouei.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-01T09:51:24.000Z","updated_at":"2025-09-07T19:23:43.000Z","dependencies_parsed_at":"2025-09-08T05:01:26.492Z","dependency_job_id":null,"html_url":"https://github.com/Asouei/safe-fetch","commit_stats":null,"previous_names":["asouei/safe-fetch"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/Asouei/safe-fetch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asouei%2Fsafe-fetch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asouei%2Fsafe-fetch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asouei%2Fsafe-fetch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asouei%2Fsafe-fetch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Asouei","download_url":"https://codeload.github.com/Asouei/safe-fetch/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asouei%2Fsafe-fetch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274135705,"owners_count":25228209,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["error-handling","fetch","http-client","npm-package","retry","timeout","typescript"],"created_at":"2025-09-06T00:20:34.185Z","updated_at":"2025-09-08T05:02:59.937Z","avatar_url":"https://github.com/Asouei.png","language":"TypeScript","funding_links":[],"categories":["Built with TypeScript"],"sub_categories":["Libraries"],"readme":"# @asouei/safe-fetch\n\n[![npm version](https://img.shields.io/npm/v/@asouei/safe-fetch.svg)](https://www.npmjs.com/package/@asouei/safe-fetch)\n[![CI](https://github.com/asouei/safe-fetch/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/asouei/safe-fetch/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![npm downloads](https://img.shields.io/npm/dm/@asouei/safe-fetch)](https://www.npmjs.com/package/@asouei/safe-fetch)\n[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)\n[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@asouei/safe-fetch)](https://bundlephobia.com/package/@asouei/safe-fetch)\n[![Zero Dependencies](https://img.shields.io/badge/dependencies-0-green.svg)](package.json)\n[![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-black?logo=codesandbox)](https://codesandbox.io/p/sandbox/fkw3z5)\n[![Awesome](https://awesome.re/badge-flat2.svg)](https://github.com/dzharii/awesome-typescript)\n\n\n*English version | [Русская версия](README.ru.md)*\n\n\u003e **Never write `try/catch` for HTTP requests again.** A complete ecosystem of type-safe HTTP utilities built around safe results and predictable error handling.\n\nModern HTTP client ecosystem that eliminates exceptions through discriminated unions, provides intelligent retries, handles timeouts properly, and integrates seamlessly with popular data fetching libraries.\n\n## 📦 Packages\n\n| Package | Version | Description |\n|---------|---------|-------------|\n| **[@asouei/safe-fetch](packages/core)** | [![npm](https://img.shields.io/npm/v/@asouei/safe-fetch.svg)](https://npmjs.com/package/@asouei/safe-fetch) | Core HTTP client with safe results, retries, and timeouts |\n| **[@asouei/safe-fetch-react-query](packages/react-query)** | [![npm](https://img.shields.io/npm/v/@asouei/safe-fetch-react-query.svg)](https://npmjs.com/package/@asouei/safe-fetch-react-query) | TanStack Query integration with optimized error handling |\n\n## 🚀 Quick Start\n\n```bash\nnpm install @asouei/safe-fetch\n```\n\n```typescript\nimport { safeFetch } from '@asouei/safe-fetch';\n\nconst result = await safeFetch.get\u003c{ users: User[] }\u003e('/api/users');\nif (result.ok) {\n  // TypeScript knows result.data is { users: User[] }\n  console.log(result.data.users);\n} else {\n  // All errors are normalized and typed\n  console.error(`${result.error.name}: ${result.error.message}`);\n}\n```\n\n## ✨ Why safe-fetch?\n\n- **🛡️ No Exceptions**: Never write `try/catch` — always get a safe result\n- **🔧 Typed Errors**: `NetworkError | TimeoutError | HttpError | ValidationError`\n- **⏱️ Smart Timeouts**: Per-attempt + total operation timeouts\n- **🔄 Intelligent Retries**: Only retries safe operations + `Retry-After` support\n- **📦 Zero Dependencies**: Tree-shakable, ~3kb, works everywhere\n- **🧪 Validation Ready**: Built-in Zod integration without exceptions\n\n## 📖 Documentation\n\n- **[Core Library](packages/core/README.md)** - Complete API documentation, examples, and migration guides\n- **[React Query Adapter](packages/react-query/README.md)** - TanStack Query integration\n\n## 🌟 Core Features\n\n### Safe Results\nEvery request returns a discriminated union - no more guessing what went wrong:\n\n```typescript\ntype SafeResult\u003cT\u003e = \n  | { ok: true; data: T; response: Response }\n  | { ok: false; error: NormalizedError; response?: Response }\n```\n\n### Normalized Errors\nAll errors follow the same structure:\n\n```typescript\n// Network issues, connection failures  \ntype NetworkError = { name: 'NetworkError'; message: string; cause?: unknown }\n\n// Request timeouts (per-attempt or total)\ntype TimeoutError = { name: 'TimeoutError'; message: string; timeoutMs: number }\n\n// HTTP 4xx/5xx responses\ntype HttpError = { name: 'HttpError'; message: string; status: number; body?: unknown }\n\n// Schema validation failures\ntype ValidationError = { name: 'ValidationError'; message: string; cause?: unknown }\n```\n\n### Smart Configuration\n\n```typescript\nimport { createSafeFetch } from '@asouei/safe-fetch';\n\nconst api = createSafeFetch({\n  baseURL: 'https://api.example.com',\n  timeoutMs: 5000,        // Per attempt\n  totalTimeoutMs: 30000,  // Total operation  \n  retries: { \n    retries: 2,\n    baseDelayMs: 300      // Exponential backoff\n  },\n  headers: { Authorization: 'Bearer token' }\n});\n\nconst result = await api.get\u003cUser[]\u003e('/users');\n```\n\n## 🔮 Ecosystem Roadmap\n\n- ✅ **Core Library** - Safe HTTP client with retries and timeouts\n- ✅ **React Query Adapter** - Optimized TanStack Query integration\n- 📋 **SWR Adapter** - SWR integration helpers\n- 🔍 **ESLint Plugin** - Enforce safe result patterns\n- 🏗️ **Framework Examples** - Next.js, Remix, Cloudflare Workers\n\n## 📱 Framework Integration\n\n### React Query\n```typescript\nimport { createSafeFetch } from '@asouei/safe-fetch';\nimport { createQueryFn, rqDefaults } from '@asouei/safe-fetch-react-query';\n\nconst api = createSafeFetch({ baseURL: '/api' });\nconst queryFn = createQueryFn(api);\n\nexport function useUsers() {\n  return useQuery({\n    queryKey: ['users'],\n    queryFn: queryFn\u003cUser[]\u003e('/users'),\n    ...rqDefaults() // { retry: false } - let safe-fetch handle retries\n  });\n}\n```\n\n### Next.js / SSR\n```typescript\n// app/users/page.tsx\nimport { safeFetch } from '@asouei/safe-fetch';\n\nexport default async function UsersPage() {\n  const result = await safeFetch.get\u003cUser[]\u003e('/api/users');\n  \n  if (!result.ok) {\n    return \u003cErrorPage error={result.error} /\u003e;\n  }\n  \n  return \u003cUserList users={result.data} /\u003e;\n}\n```\n\n### Cloudflare Workers\n```typescript\nexport default {\n  async fetch(request: Request) {\n    const result = await safeFetch.get\u003c{ status: string }\u003e('https://api.service.com/health');\n    \n    return new Response(\n      result.ok ? JSON.stringify(result.data) : result.error.message,\n      { status: result.ok ? 200 : 500 }\n    );\n  }\n};\n```\n\n## 🤝 Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n\n**Quick development setup:**\n```bash\ngit clone https://github.com/asouei/safe-fetch.git\ncd safe-fetch\npnpm install\npnpm -r test\npnpm -r build\n```\n\n## 📄 License\n\nMIT © [Aleksandr Mikhailishin](https://github.com/asouei)\n\n---\n\n**Built with ❤️ for developers who value predictable, type-safe HTTP clients.**","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasouei%2Fsafe-fetch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fasouei%2Fsafe-fetch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasouei%2Fsafe-fetch/lists"}