{"id":13902759,"url":"https://github.com/eser/fetchp","last_synced_at":"2025-04-11T21:14:39.254Z","repository":{"id":56785266,"uuid":"524598098","full_name":"eser/fetchp","owner":"eser","description":"Not an another HTTP client but a fetch wrapper with fluent API and superpowers","archived":false,"fork":false,"pushed_at":"2023-01-07T21:37:03.000Z","size":420,"stargazers_count":105,"open_issues_count":4,"forks_count":7,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-11T21:14:34.144Z","etag":null,"topics":["abortable","acikkaynak","fetch","fetch-api","fetch-mock","hooks","interceptor","json","middleware","mock","protobuf","react","request","response","testable"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eser.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null},"funding":{"github":["eserozvataf"],"patreon":"eserozvataf","open_collective":"eser"}},"created_at":"2022-08-14T07:04:30.000Z","updated_at":"2024-12-16T21:02:44.000Z","dependencies_parsed_at":"2023-02-08T02:18:35.197Z","dependency_job_id":null,"html_url":"https://github.com/eser/fetchp","commit_stats":null,"previous_names":["eserozvataf/fetchp"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eser%2Ffetchp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eser%2Ffetchp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eser%2Ffetchp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eser%2Ffetchp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eser","download_url":"https://codeload.github.com/eser/fetchp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248480427,"owners_count":21110937,"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","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":["abortable","acikkaynak","fetch","fetch-api","fetch-mock","hooks","interceptor","json","middleware","mock","protobuf","react","request","response","testable"],"created_at":"2024-08-06T22:01:23.014Z","updated_at":"2025-04-11T21:14:39.226Z","avatar_url":"https://github.com/eser.png","language":"TypeScript","readme":"[![fetchp][logo-image]](https://github.com/eserozvataf/fetchp)\n\n[![npm version][npm-image]][npm-url]\n[![npm download][npm-download-image]][npm-url]\n[![license][license-image]][license-url]\n\n## Table of Contents\n\n- [What is the fetchp/fetch+?](#what-is-the-fetchpfetch)\n- [Why? What's the motivation?](#why-whats-the-motivation)\n- [Quick start](#quick-start)\n- [Usage](#usage)\n  - [Basic HTTP Request For Text-Based Data](#basic-http-request-for-text-based-data)\n  - [Basic HTTP Request For JSON Data](#basic-http-request-for-json-data)\n  - [Caching Requests](#caching-requests)\n  - [Aborting a Request](#aborting-a-request)\n  - [Setting a Base URL for Requests](#setting-a-base-url-for-requests)\n  - [Middlewares / Hooks](#middlewares--hooks)\n  - [Middlewares / Hooks (URL Based)](#middlewares--hooks-url-based)\n  - [On-Demand Fetching](#on-demand-fetching)\n  - [Mocking an URL for Request](#mocking-an-url-for-request)\n  - [Mocking for Testing (Buggy ATM)](#mocking-for-testing-buggy-atm)\n  - [Using with _**React Hooks**_](#using-with-react-hooks)\n  - [Using with _**React Hooks**_, mocking for testing](#using-with-react-hooks-mocking-for-testing)\n  - [Using with _**React Hooks**_, manual fetching](#using-with-react-hooks-manual-fetching)\n  - [Using with _**Deno**_](#using-with-deno)\n- [Todo List](#todo-list)\n- [Requirements](#requirements)\n- [License](#license)\n- [Contributing](#contributing)\n- [To Support](#to-support)\n\n## What is the fetchp/fetch+?\n\n**fetchp** is \"not an another HTTP client but a fetch wrapper with fluent API\nand superpowers\". The trailing \"p\" is a means for \"plus\".\n\n### Why? What's the motivation?\n\n[fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) is a\nstandard Web API that's already supported by modern browsers, Deno, node.js, bun\nand etc.\n\nWe don't need another HTTP client. Still, a web API's target audience is very\nbroad. APIs like fetch are being designed carefully by the community and\norganizations to be used by the general public. Any design mistake can cause\nlots of unrecoverable problems. For this reason, they're keeping it simple and\nrunning away from the idea of increasing the extra features they can cover.\n\nSo, if you want to use fetch, you may need to wrap it with some extra\nfunctionality from time to time. For example, if you call it from a React web\nproject, checking its loading state almost is a must. Or, if you're writing\nautomated tests for your application, you need to mock the fetch API.\n\nThis is where fetchp comes into play. It still uses fetch's native\nimplementation that brought you by browsers or runtimes themselves, but in the\nmeantime, it wraps the fetch to provide extra functionality for developers.\n\nFetchp tries to assemble some tools that are useful and reusable for most of the\nprojects that fetch doesn't provide.\n\nMoreover, since fetchp is a JavaScript module / npm package, it also follows the\nsemantic versioning and its API can be evolved in the future without breaking\nany project.\n\n## Superpowers\n\n- [x] Fluent API\n- [x] React Hooks API\n- [x] Abortable requests\n- [x] Testing-friendly\n- [x] Mocking response for requests (not just for tests)\n- [x] Setting Base URL\n- [x] Automatic deserialization/parsing by content-types\n- [x] Prefetching and local caching\n- [x] On-demand fetching\n- [x] Fetch status\n- [x] Multiple instances of fetchp\n- [x] TypeScript Type Support\n\nMore to come see [Todo List](#todo-list) section.\n\n## Quick start\n\nExecute `npm install fetchp` or `yarn add fetchp` to install fetchp and its\ndependencies into your project directory.\n\n## Usage\n\n### Basic HTTP Request For Text-Based Data\n\n```js\nimport { fetchp } from \"fetchp\";\n\n// you don't need to await request\nconst req = fetchp.request(\"GET\", \"https://www.google.com/\");\n\n// you may await data instead...\n// once your request is completed, it will return a string\nconsole.log(await req.data);\n```\n\n### Basic HTTP Request For JSON Data\n\n```js\nimport { fetchp } from \"fetchp\";\n\nconst req = fetchp.request(\"GET\", \"https://jsonplaceholder.typicode.com/posts\");\n\n// since your request will return a json, req.data will return an object\nconsole.log(await req.data);\n```\n\n### Caching Requests\n\n```js\nimport { fetchp, FetchpStatus } from \"fetchp\";\n\nconst doReq = () =\u003e\n  fetchp.request(\n    \"GET\",\n    \"https://jsonplaceholder.typicode.com/posts\",\n    { cacheRequest: true },\n  );\n\nconst response1 = doReq();\nawait new Promise((r) =\u003e setTimeout(r, 1000));\nconst response2 = doReq();\n\n// you can check equality afterwars\nassert(await response1.data === await response2.data);\n```\n\n### Aborting a Request\n\n```js\nimport { fetchp, FetchpStatus } from \"fetchp\";\n\nconst req = fetchp.request(\"GET\", \"https://jsonplaceholder.typicode.com/posts\");\n\n// abort it after 500 milliseconds\nsetTimeout(() =\u003e req.abortController.abort(), 500);\n\n// you can check status afterwars\nassert(req.status === FetchpStatus.CANCELED);\n```\n\n### Setting a Base URL for Requests\n\nAssume that you're working with a single API on the backend, and you don't want\nto repeat yourself by concatenating endpoint URL strings in each request you\nmake.\n\n```js\nimport { fetchp } from \"fetchp\";\n\nfetchp.setBaseUri(\"https://jsonplaceholder.typicode.com\");\n\nconst req = fetchp.request(\"GET\", \"/posts\");\n\nconsole.log(await req.data);\n```\n\n### Middlewares / Hooks\n\nAssume that you're need to add additional headers to each request you make.\n\n```js\nimport { fetchp, FetchpHookType } from \"fetchp\";\n\nfetchp.hooks.add(\n  FetchpHookType.BuildRequestHeaders,\n  (headers) =\u003e headers.set(\"Authorization\", `Bearer ${getIdToken()}`);\n);\n\nconst response = fetchp.request(\n  \"GET\",\n  \"https://localhost/api/some-restricted-endpoint\"\",\n);\n```\n\n### Middlewares / Hooks (URL Based)\n\nAssume that you're need to observe state changes only for the urls in your\nfilter.\n\n```js\nimport { fetchp, FetchpHookType } from \"fetchp\";\n\nfetchp.hooks.addForUrl(\n  FetchpHookType.StateChange,\n  [\"GET\", \"POST\"],\n  /^https:\\/\\/jsonplaceholder\\.typicode\\.com\\//,\n  (request, status) =\u003e\n    console.log(`[state change] ${request.url} -\u003e ${status}`),\n);\n\nconst response = fetchp.request(\n  \"GET\",\n  \"https://jsonplaceholder.typicode.com/todos\",\n);\n```\n\n### On-Demand Fetching\n\nAssume that you don't want to invoke the request immediately. You'll set up an\nexternal trigger for this.\n\n```js\nimport { fetchp } from \"fetchp\";\n\nconst req = fetchp.request(\"GET\", \"/posts\", { immediate: false });\n\nsetTimeout(() =\u003e req.exec(), 500);\n\nconsole.log(await req.data);\n```\n\n### Mocking an URL for Request\n\nAssume that your API is not yet built on the backend, and you want to mock its\nbehavior.\n\n```js\nimport { fetchp } from \"fetchp\";\n\nconst mockContent = { hello: \"world\" };\nconst mockResponse = (request) =\u003e\n  new Response(\n    JSON.stringify(mockContent),\n    {\n      status: 200,\n      statusText: \"OK\",\n      headers: {\n        \"content-type\": \"application/json\",\n      },\n    },\n  );\n\nfetchp.mocks.add([\"GET\", \"POST\"], \"/hello\", mockResponse);\n\n// mocking is done, let's make a request to the mocked URL\nconst req = fetchp.request(\"GET\", \"/hello\");\n\n// it will return { hello: \"world\" }\nconsole.log(await req.data);\n```\n\n### Mocking for Testing (Buggy ATM)\n\nAssume that you want to mock your code without dealing with a test framework and\nits interfaces / methods. All you need to do is importing `fetchp/mock` instead\nof `fetchp` module.\n\n```js\n// just replace fetchp with fetchp/mock\nimport { fetchp } from \"fetchp/mock\";\n\nconst req = fetchp.request(\"GET\", \"/posts\");\n\nconsole.log(await req.data);\n```\n\n### Using with _**React Hooks**_\n\n```js\nimport { useFetchp } from \"fetchp\";\n\nconst MyComponent = (props) =\u003e {\n  const { data, isLoading, error } = useFetchp(\"GET\", \"/posts\");\n\n  if (isLoading) {\n    return \u003cdiv\u003eLoading...\u003c/div\u003e;\n  }\n\n  if (error) {\n    return \u003cdiv\u003eError: {error.message}\u003c/div\u003e;\n  }\n\n  return \u003cdiv\u003e{JSON.stringify(data)}\u003c/div\u003e;\n};\n```\n\n### Using with _**React Hooks**_, mocking for testing\n\n```js\n// just replace fetchp with fetchp/mock\nimport { useFetchp } from \"fetchp/mock\";\n\nconst MyComponent = (props) =\u003e {\n  const { data, isLoading, error } = useFetchp(\"GET\", \"/posts\");\n\n  if (isLoading) {\n    return \u003cdiv\u003eLoading...\u003c/div\u003e;\n  }\n\n  if (error) {\n    return \u003cdiv\u003eError: {error.message}\u003c/div\u003e;\n  }\n\n  return \u003cdiv\u003e{JSON.stringify(data)}\u003c/div\u003e;\n};\n```\n\n### Using with _**React Hooks**_, manual fetching\n\n```js\nimport { useEffect } from \"react\";\nimport { useFetchp } from \"fetchp\";\n\nconst MyComponent = (props) =\u003e {\n  const { data, status, isSuccess, doFetch } = useFetchp(\n    \"GET\",\n    \"/posts\",\n    false,\n  );\n\n  useEffect(() =\u003e {\n    // fetch data after 500 milliseconds have passed\n    setTimeout(() =\u003e doFetch(), 500);\n  }, []);\n\n  if (!isSuccess) {\n    return \u003cdiv\u003eStatus: {status}...\u003c/div\u003e;\n  }\n\n  return \u003cdiv\u003e{status}: {JSON.stringify(data)}\u003c/div\u003e;\n};\n```\n\n### Using with _**Deno**_\n\n```js\nimport { fetchp } from \"npm:fetchp\";\n\nconst req = fetchp.request(\"GET\", \"https://www.google.com/\");\n\nconsole.log(await req.data);\n```\n\n## Todo List\n\nSee [GitHub Projects](https://github.com/eserozvataf/fetchp/projects) for more.\n\n- [ ] Clean cache storage\n- [ ] Fixing the bug in `fetchp/mock` module\n- [ ] Add advanced support for hooks / middlewares / interceptors\n- [ ] Protobuf support\n- [ ] Registering serializers / deserializers by content-type\n- [ ] Logging adapters\n- [ ] MAYBE: Reducers / Actions?\n- [ ] Mechanism for request retries\n\n## Requirements\n\n- node.js (https://nodejs.org/)\n- Deno (to run tests and contribution) (https://deno.land/)\n\n## License\n\nApache 2.0, for further details, please see [LICENSE](LICENSE) file\n\n## Contributing\n\nSee [contributors.md](contributors.md)\n\nIt is publicly open for any contribution. Bugfixes, new features and extra\nmodules are welcome.\n\n- To contribute to code: Fork the repo, push your changes to your fork, and\n  submit a pull request.\n- To report a bug: If something does not work, please report it using\n  [GitHub Issues](https://github.com/eserozvataf/fetchp/issues).\n\n## To Support\n\n[Visit my GitHub Sponsors profile at github.com/sponsors/eserozvataf](https://github.com/sponsors/eserozvataf)\n\n[npm-image]: https://img.shields.io/npm/v/fetchp.svg?style=flat-square\n[npm-download-image]: https://img.shields.io/npm/dt/fetchp.svg?style=flat-square\n[npm-url]: https://www.npmjs.com/package/fetchp\n[license-image]: https://img.shields.io/npm/l/fetchp.svg?style=flat-square\n[license-url]: https://github.com/eserozvataf/fetchp/blob/master/LICENSE\n[logo-image]: https://raw.githubusercontent.com/eserozvataf/fetchp/master/etc/fetchp.svg\n","funding_links":["https://github.com/sponsors/eserozvataf","https://patreon.com/eserozvataf","https://opencollective.com/eser"],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feser%2Ffetchp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feser%2Ffetchp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feser%2Ffetchp/lists"}