{"id":16368004,"url":"https://github.com/fzn0x/hypf","last_synced_at":"2026-02-08T23:01:36.638Z","repository":{"id":288675956,"uuid":"771190527","full_name":"fzn0x/hypf","owner":"fzn0x","description":" 🤏 Small (2.5kB MINIFIED + GZIPPED \u0026 0 dependencies) and strong-typed HTTP client for Deno, Bun, Node.js, Cloudflare Workers and Browsers.","archived":false,"fork":false,"pushed_at":"2024-07-16T22:10:39.000Z","size":570,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-04T13:58:01.764Z","etag":null,"topics":["4kb","bun","fetch","fetch-api","fetch-wrapper","frontend","http","http-client","http-request","http-requests","javascript","js","json","lightweight","nodejs","reactjs","request","rest","typescript"],"latest_commit_sha":null,"homepage":"https://bundlephobia.com/package/hypf@0.2.3","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/fzn0x.png","metadata":{},"created_at":"2024-03-12T21:13:42.000Z","updated_at":"2025-04-15T08:51:36.000Z","dependencies_parsed_at":"2025-04-21T12:15:27.658Z","dependency_job_id":null,"html_url":"https://github.com/fzn0x/hypf","commit_stats":null,"previous_names":["fzn0x/hypf"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fzn0x/hypf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fzn0x%2Fhypf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fzn0x%2Fhypf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fzn0x%2Fhypf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fzn0x%2Fhypf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fzn0x","download_url":"https://codeload.github.com/fzn0x/hypf/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fzn0x%2Fhypf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29248487,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-08T22:49:53.206Z","status":"ssl_error","status_checked_at":"2026-02-08T22:49:51.384Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["4kb","bun","fetch","fetch-api","fetch-wrapper","frontend","http","http-client","http-request","http-requests","javascript","js","json","lightweight","nodejs","reactjs","request","rest","typescript"],"created_at":"2024-10-11T02:51:24.065Z","updated_at":"2026-02-08T23:01:36.623Z","avatar_url":"https://github.com/fzn0x.png","language":"TypeScript","readme":"\u003cp align=\"center\" width=\"100%\"\u003e\n    \u003cimg width=\"55%\" src=\"./assets/hyperfetch.png\"\u003e\n\u003c/p\u003e\n\n\u003chr /\u003e\n\n[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/fzn0x/hypf/ci.yml?branch=main)](https://github.com/fzn0x/hypf/actions)\n[![GitHub](https://img.shields.io/github/license/fzn0x/hypf)](https://github.com/fzn0x/hypf/blob/main/LICENSE)\n[![npm](https://img.shields.io/npm/v/hypf)](https://www.npmjs.com/package/hypf)\n[![npm](https://img.shields.io/npm/dm/hypf)](https://www.npmjs.com/package/hypf)\n[![JSR](https://jsr.io/badges/@fzn0x/hypf)](https://jsr.io/@fzn0x/hypf)\n[![Bundle Size](https://img.shields.io/bundlephobia/min/hypf)](https://bundlephobia.com/result?p=hypf)\n[![Bundle Size](https://img.shields.io/bundlephobia/minzip/hypf)](https://bundlephobia.com/result?p=hypf)\n[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/fzn0x/hypf)](https://github.com/fzn0x/hypf/pulse)\n[![GitHub last commit](https://img.shields.io/github/last-commit/fzn0x/hypf)](https://github.com/fzn0x/hypf/commits/main)\n\n\u003chr /\u003e\n\nSmall (7.2kB minified \u0026 0 dependencies) and type-powered HTTP client for Deno, Bun, Node.js, Cloudflare Workers and Browsers. 🚀\n\nThe most flexible fetch wrapper that allows you to have more than one practice to get things done!\n\n## Table of Contents\n\n- [Get Started](#get-started)\n- [Error Handling](#error-handling)\n- [Response Cloning](#response-cloning)\n- [Request Cloning \u0026 Dry Run](#request-cloning--dry-run)\n- [Hooks](#hooks)\n- [Retry Mechanism](#retry-mechanism)\n- [Infer Response Types](#infer-response-types)\n- [URLSearchParams](#urlsearchparams)\n- [Form Data](#form-data)\n- [AbortController](#abortcontroller)\n- [Debug](#debug)\n- [Acknowledgements](#acknowledgements)\n- [License](#license)\n\n## Get Started\n\n```sh\n# Node.js\nnpm install hypf\n# Bun\nbun install hypf\n```\n\nThe idea of this tool is to provide lightweight `fetch` wrapper for Node.js, Bun:\n\n```ts\nimport hypf from 'hypf'\n\nconst hypfRequest = hypf.init('https://jsonplaceholder.typicode.com')\n\n// Example usage of POST method with retry and timeout\nconst [postErr, postData] = await hypfRequest.post(\n  '/posts',\n  { retries: 3, timeout: 5000 },\n  {\n    title: 'foo',\n    body: 'bar',\n    userId: 1,\n  }\n)\n\nif (postErr) {\n  console.error('POST Error:', postErr)\n} else {\n  console.log('POST Data:', postData)\n}\n```\n\nCloudflare Workers:\n\n```ts\nexport default {\n  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise\u003cResponse\u003e {\n    const hypfInstance = await hypf.init('https://jsonplaceholder.typicode.com')\n\n    const [getErr, getData] = await hypfInstance.get\u003c\n      Array\u003c{\n        userId: number\n        id: number\n        title: string\n        body: string\n      }\u003e\n    \u003e('/posts')\n\n    if (getErr) {\n      console.error('GET Error:', getErr)\n    }\n\n    return Response.json(getData)\n  },\n}\n```\n\nand Browsers:\n\n```html\n\u003cscript src=\"https://unpkg.com/hypf/browser/hyperfetch-browser.min.js\"\u003e\u003c/script\u003e\n\n\u003cscript\u003e\n  ;(async () =\u003e {\n    const request = hypf.default.init('https://jsonplaceholder.typicode.com')\n  })()\n\u003c/script\u003e\n```\n\n## Error Handling\n\nYou can handle errors like [Error handling on Golang](https://go.dev/blog/error-handling-and-go)\n\n```ts\nconst hypfRequest = hypf.init('https://jsonplaceholder.typicode.com')\n\n// Example usage of POST method with retry and timeout\nconst [postErr, postData] = await hypfRequest.post(\n  '/posts',\n  { retries: 3, timeout: 5000 },\n  {\n    title: 'foo',\n    body: 'bar',\n    userId: 1,\n  }\n)\n\nif (postErr) {\n  console.error('POST Error:', postErr)\n} else {\n  console.log('POST Data:', postData)\n}\n```\n\nor since `v0.2.2` throw on error with `throwOnError` options sets `true`\n\n```ts\nconst hypfRequest = hypf.init('https://jsonplaceholder.typicode.com')\n\ntry {\n  const response = await hypfRequest.post(\n    '/posts',\n    { retries: 3, timeout: 5000, initOptions: { throwOnError: true } },\n    {\n      title: 'foo',\n      body: 'bar',\n      userId: 1,\n    }\n  )\n\n  console.log(response)\n} catch (err) {\n  console.log(err)\n}\n```\n\n## Response Cloning\n\nYou need to use `throwOnError: true` to enable Response clone feature.\n\n```ts\nconst res = await hypfRequest.get(\n  'https://jsonplaceholder.typicode.com/todos/1',\n  {},\n  {},\n  {\n    throwOnError: true,\n  }\n)\n\nconst res2 = res.clone()\n\nconsole.log(res2)\n```\n\n## Request Cloning \u0026 Dry Run\n\nDry Run is supported in hypf, you can calling hypf requests without executing it!\n\nThis enables more features in the future and also Request cloning:\n\n```ts\nconst req = await hypfRequest.get(\n  'https://jsonplaceholder.typicode.com/todos/1',\n  {\n    dryRun: true,\n  },\n  {},\n  {\n    throwOnError: true,\n  }\n)\n\nconst req2 = req.clone()\n\nconsole.log(req2)\n```\n\n## Hooks\n\nHooks is supported and expected to not modifying the original result by design.\n\n```ts\nconst hooks = {\n  preRequest: (url, options) =\u003e {\n    console.log(`Preparing to send request to: ${url}`)\n    // You can perform actions before the request here\n  },\n  postRequest: (url, options, data, response) =\u003e {\n    console.log(`Request to ${url} completed with status: ${response?.[0] ? 'error' : 'success'}`)\n    // You can perform actions after the request here, including handling errors\n  },\n}\n\nconst requestWithHooks = hypf.init('https://jsonplaceholder.typicode.com', { hooks })\n\n// Example usage of POST method with retry and timeout\nconst [postErr, postData] = await requestWithHooks.post(\n  '/posts',\n  { retries: 3, timeout: 5000 },\n  {\n    title: 'foo',\n    body: 'bar',\n    userId: 1,\n  }\n)\n```\n\nList of Hooks:\n\n```ts\nexport interface Hooks {\n  preRequest?: (url: string, options: RequestOptions) =\u003e void\n  postRequest?: \u003cT, U\u003e(\n    url: string,\n    options: RequestOptions,\n    data?: T,\n    response?: [Error | null, U]\n  ) =\u003e void\n  preRetry?: (url: string, options: RequestOptions, retryCount: number, retryLeft: number) =\u003e void\n  postRetry?: \u003cT, U\u003e(\n    url: string,\n    options: RequestOptions,\n    data?: T,\n    response?: [Error | null, U],\n    retryCount?: number,\n    retryLeft?: number\n  ) =\u003e void\n  preTimeout?: (url: string, options: RequestOptions) =\u003e void\n  postTimeout?: (url: string, options: RequestOptions) =\u003e void\n}\n```\n\n## Retry Mechanism\n\nYou can retry your request once it's failed!\n\n```ts\nconst [postErr, postData] = await requestWithHooks.post(\n  '/posts',\n  { retries: 3, timeout: 5000 },\n  {\n    title: 'foo',\n    body: 'bar',\n    userId: 1,\n  }\n)\n```\n\nJitter and backoff also supported. 😎\n\n```ts\nconst [postErr, postData] = await requestWithHooks.post(\n  '/posts',\n  { retries: 3, timeout: 5000, jitter: true }, // false `jitter` to use backoff\n  {\n    title: 'foo',\n    body: 'bar',\n    userId: 1,\n  }\n)\n```\n\nYou can modify backoff and jitter factor as well.\n\n```ts\nconst [postErr, postData] = await requestWithHooks.post(\n  '/posts',\n  { retries: 3, timeout: 5000, jitter: true, jitterFactor: 10000 }, // false `jitter` to use backoff\n  {\n    title: 'foo',\n    body: 'bar',\n    userId: 1,\n  }\n)\n\n// or backoff\n\nconst [postErr, postData] = await requestWithHooks.post(\n  '/posts',\n  { retries: 3, timeout: 5000, jitter: false, backoffFactor: 10000 }, // false `jitter` to use backoff\n  {\n    title: 'foo',\n    body: 'bar',\n    userId: 1,\n  }\n)\n```\n\nRetry on timeout also supported.\n\n```ts\nconst [postErr, postData] = await requestWithHooks.post(\n  '/posts',\n  { retries: 3, timeout: 5000, retryOnTimeout: true },\n  {\n    title: 'foo',\n    body: 'bar',\n    userId: 1,\n  }\n)\n```\n\n## Infer Response Types\n\n```ts\nconst [getErr, getData] = await hypfRequest.get\u003c\n  Array\u003c{\n    userId: number\n    id: number\n    title: string\n    body: string\n  }\u003e\n\u003e('/posts', {\n  retries: 3,\n  timeout: 5000,\n})\n\ngetData?.[0]?.id // number | undefined\n```\n\n## URLSearchParams\n\n```ts\nconst [getErr, getData] = await hypfRequest.get('/posts', {\n  retries: 3,\n  timeout: 5000,\n  params: {\n    id: 1,\n  },\n}) // /posts?id=1\n```\n\n## Form Data\n\nExample usecase for Upload File:\n\n```ts\nexport async function postImportFile(formData: FormData) {\n  const [postErr, postData] = await hypfRequest.post(`/api/upload-file/import`, {\n    body: formData,\n  })\n\n  if (postErr) {\n    throw postErr\n  }\n\n  return postData\n}\n```\n\n## AbortController\n\nWe expose abort controller, you can cancel next request anytime.\n\n```ts\n// DELETE will not work if you uncomment this\nconst controller = requestWithHooks.getAbortController()\n\ncontroller.abort()\n\n// Example usage of DELETE method with retry and timeout\nconst [deleteErr, deleteData] = await requestWithHooks.delete('/posts/1', {\n  retries: 3,\n  timeout: 5000,\n})\n\nif (deleteErr) {\n  console.error('DELETE Error:', deleteErr)\n} else {\n  console.log('DELETE Data:', deleteData)\n}\n```\n\n## Debugging\n\nDebugging the library is possible but limited. You can pass `true` to `debug` option:\n\n```ts\nconst requestWithHooks = hypf.init('https://jsonplaceholder.typicode.com', { debug: true })\n```\n\nThis is designed to be useful to track an issue. Submit a PR to debug more areas in the code!\n\n## Acknowledgements\n\nHyperfetch is highly inspired by [Hono](https://github.com/honojs/hono), Got, Ky and Axios\n\n## License\n\nHyperfetch is [MIT-licensed](./LICENSE) and Open Source Software by [fzn0x](https://github.com/fzn0x) and [contributors from Hono and the Hyperfetch community](https://github.com/fzn0x/hypf/graphs/contributors):\n\n\u003ca href=\"https://github.com/fzn0x/hypf/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=fzn0x/hypf\" /\u003e\n\u003c/a\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffzn0x%2Fhypf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffzn0x%2Fhypf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffzn0x%2Fhypf/lists"}