{"id":18823606,"url":"https://github.com/futpib/fetish","last_synced_at":"2025-08-13T14:08:36.457Z","repository":{"id":24551888,"uuid":"101896495","full_name":"futpib/fetish","owner":"futpib","description":"fetch-based flexible http client","archived":false,"fork":false,"pushed_at":"2025-04-09T19:49:10.000Z","size":4529,"stargazers_count":9,"open_issues_count":11,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-09T20:40:01.075Z","etag":null,"topics":["fetch-api","http-client","javascript","nodejs","promise"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/futpib.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2017-08-30T15:12:20.000Z","updated_at":"2024-12-11T22:41:35.000Z","dependencies_parsed_at":"2023-01-14T07:46:06.976Z","dependency_job_id":"6c627f1b-f379-4c6a-bf4a-fa2c3d058faa","html_url":"https://github.com/futpib/fetish","commit_stats":{"total_commits":74,"total_committers":2,"mean_commits":37.0,"dds":0.2702702702702703,"last_synced_commit":"269fe4e4029e54c5b07f58084d3a764ce69ccdd7"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/futpib%2Ffetish","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/futpib%2Ffetish/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/futpib%2Ffetish/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/futpib%2Ffetish/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/futpib","download_url":"https://codeload.github.com/futpib/fetish/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248807393,"owners_count":21164680,"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":["fetch-api","http-client","javascript","nodejs","promise"],"created_at":"2024-11-08T00:54:09.677Z","updated_at":"2025-04-14T01:30:56.053Z","avatar_url":"https://github.com/futpib.png","language":"JavaScript","readme":"# fetish\n\n\u003e Fetch the way you desire\n\n[![npm](https://shields.io/npm/v/fetish)](https://www.npmjs.com/package/fetish) [![Build Status](https://travis-ci.org/futpib/fetish.svg?branch=master)](https://travis-ci.org/futpib/fetish) [![Coverage Status](https://coveralls.io/repos/github/futpib/fetish/badge.svg?branch=master)](https://coveralls.io/github/futpib/fetish?branch=master) [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo) [![Gitter](https://badges.gitter.im/join_chat.svg)](https://gitter.im/futpib-fetish)\n\n## Why fetish?\n\n- [Drop-in fetch replacement](#drop-in-fetch-replacement)\n- [No fetch quirks](#no-fetch-quirks)\n- [Plugins and middlewares](#plugins-and-middlewares)\n- [Use out of the box](#out-of-the-box)\n- [Pick the features you need](#custom-anything)\n- [Customize anything](#custom-plugins)\n\n## Example\n\n```js\nimport {fetish, baseUrl, defaultHeaders} from 'fetish';\n\nconst client = fetish\n\t.with(baseUrl('http://example.org/api/v2'))\n\t.with(defaultHeaders({\n\t\t'X-API-Key': 'secret'\n\t}));\n\nclient.post('/posts', {\n\tbody: {\n\t\ttitle: 'me',\n\t\tbody: 'likey'\n\t}\n});\n```\n\n## Usage\n\n### Out of the Box\n\n```\nnpm i fetish\n```\n\nOr, if you prefer, with [Yarn](https://yarnpkg.com/):\n\n```\nyarn add fetish\n```\n\nBoxed fetish pulls in [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) and [es6-promise](https://github.com/stefanpenner/es6-promise) polyfills, but of course [you can override both of those](#custom-promise), use [`fetish-peer` (with peer dependency)](#fetish-with-peer-dependencies) or even [pick and choose features you want with `fetish-nude`](#custom-anything).\n\n#### Custom Promise\n\n```js\nimport {fetish, customPromise} from 'fetish';\nimport Promise from 'bluebird';\n\nconst client = fetish.with(customPromise(Promise));\n\nclient('/too-fast').delay(100); // use bluebird's promise methods\n```\n\n#### Custom fetch\n\n```js\nimport {fetish, customFetch} from 'fetish';\nimport fetch from 'fetch-ie8';\n\nconst client = fetish.with(customFetch(fetch));\n```\n\n\n### Custom Anything\n\nHere is a glimpse of how fetish is implemented. It starts with [fetish-nude](https://github.com/futpib/fetish/tree/master/packages/fetish-nude) which is a simple wrapper around fetch with only one extra method called `with` used to add plugins. Plugins, which are just functions from one fetish to a better fetish, are then added to it.\n\nLet's build a barebone http client with only one added feature: it will `JSON.stringify` the request body.\n\n```\nnpm i fetish-nude fetish-plugin-fetish-plugin-serialize-body-to-json\n```\n\n```js\nexports.fetish = fetishNude\n\t.with(serializeBodyToJson);\n```\n\nIf none of the [existing plugins](https://www.npmjs.com/search?q=fetish-plugin-*) suits your fancy, [creating a custom plugin](#custom-plugins) is trivial.\n\n\n### Fetish with peer dependencies\n\nIf you don't want to use `isomorphic-fetch` or `es6-promise` polyfills that come as dependencies with the `fetish` package, use `fetish-peer` instead.\n\nObviously, if you choose `fetish-peer` package, it is expected that your runtime has native fetch and Promise support or that you install fetch and Promise polyfills of your choice separately.\n\n## Plugins and Middlewares\n\nPlugins are middlewares. Whoa, right?\n\nThe complete list can be found in [packages](https://github.com/futpib/fetish/tree/master/packages/) directory and [on npm](https://www.npmjs.com/search?q=keywords:fetish).\n\n#### Drop-in fetch replacement\n\n`fetish.fetch` tries to be as `fetch`-compatible as it can.\n\nIf you are [going full custom](#custom-anything) and need this, use `fetish-plugin-fetch-drop-in`.\n\n### No fetch quirks\n\nWhat quirks? Fetch is awesome, right? Almost right.\n\n#### Multiple consumers of a Response\n\nWhile Promise is [multicast](https://github.com/kriskowal/gtor#singular-and-plural), [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) is not. You can pass a promise to as many consumers as you like and they can call `promise.then` multiple times, but while you can pass a response to many consumers, only the first call of `response.json()` can ever be successful. There are [good reasons for that](https://github.com/whatwg/fetch/issues/196#issuecomment-171935172), but there are also good reasons not to want that.\n\nThis is precisely what `fetish-plugin-multicast-response` fixes by monkey-patching all stream-reading methods. Unfortunately, if you try to read a response body in multiple ways (like doing `r.json()` and then also `r.text()`) you'd still get an error.\n\n### Custom Plugins\n\nFetish plugins are functions from one fetish to a better fetish. And fetish is a function from request options to a Promise of a Response. This gives plugins the power to change anything about fetish: options, response or the fetish function itself.\n\nApproximate type signature:\n\n```js\ntype Options = { /* url, body, query, headers, etc. */ };\ntype Fetish = Options =\u003e Promise\u003cResponse\u003e;\ntype Plugin = Fetish =\u003e Fetish;\n```\n\n#### Simplest possible plugin\n\nThis plugin changes nothing about the fetish, but hopefully serves as a good first example.\n\n```js\nconst identity = nextFetish =\u003e options =\u003e nextFetish(options).then(response =\u003e response);\nconst client = fetish.with(identity);\n```\n\nYou can see from this example and the types above that plugins can easily implement middlewares in this setting.\n\n#### Transforming request\n\nSay you want to add a custom header to every request. You could use `fetch-plugin-default-options`, but we will do it the hard way instead.\n\n```js\nimport {defaultsDeep} from 'lodash/fp';\n\n// Easy mode\nconst myHeaders = nextFetish =\u003e options =\u003e {\n\treturn nextFetish(defaultsDeep(options, {\n\t\theaders: {\n\t\t\t'X-Hello': 'World'\n\t\t}\n\t}));\n};\n\n// A bit more generic\nconst defaultHeaders = headers =\u003e nextFetish =\u003e options =\u003e nextFetish(defaultsDeep(options {headers}));\n\nconst client = fetish\n\t.with(myHeaders)\n\t.with(defaultHeaders({\n\t\t'X-Beep': 'Boop'\n\t}));\n```\n\n#### Transforming response\n\nLet's make a plugin that lets one parse a response into an [immutable.js](https://github.com/facebook/immutable-js) object. The real implementation is called `fetish-plugin-immutable-response`.\n\nWe are going to add `response.immutable()` method as a simple wrapper around `response.json()`.\n\nGenerally, actually modifying responses is a bit tricky because of the way [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) is defined. It's immutable and it does not define any \"copying setter\" methods. One could use [`new Response()`](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response), but in reality I would prefer monkey-patching the response anyway (sorry).\n\n```js\nimport {fromJS} from 'immutable';\n\nconst immutable = nextFetish =\u003e options =\u003e nextFetish(options).then(response =\u003e {\n\tresponse.immutable = () =\u003e response.json().then(fromJS);\n\treturn response;\n});\n\nconst client = fetish.with(immutable);\n\n// Somewhere else:\nconst treasure = await client('/treasure/1').then(r =\u003e r.immutable());\n```\n\nNote that you can just as easily throw an error from the `.then` callback of your plugin. This way you can turn erroneous responses into exceptions, and that's actually what `fetish-plugin-throw-http-errors` does. Or use `.catch` in a simillar way, maybe you want to recover from errors, whatever, you got the point.\n\n#### Adding new methods\n\nHow do you add custom methods like `fetch.post` though?\n\n```js\nconst postMethod = nextFetish =\u003e Object.assign(nextFetish, {\n\tpost: function (options) {\n\t\t// `this` here will be the final fetish someone calls like `fetish.post(...)`\n\t\treturn this(Object.assign({}, options, {\n\t\t\tmethod: 'POST'\n\t\t}));\n\t}\n});\n\nconst client = fetish.with(postMethod);\n\n// Later:\nconst response = await client.post({\n\turl: '/posts/',\n\tbody: { i: 'post' }\n});\n```\n\nBut of cource this basic stuff is already impletemented in `fetish-plugin-http-methods`. Hope you come up with something more exciting!\n\n#### Be nice :3\n\nConsider contributing you plugins and stuff to this repo.\n\nIf you deicide to publish your plugins to npm separately from this repo, please try to be careful about the \"fetish-*\" namespace-like thingy.\n\n## Contributing\n\n1. Fork this\n2. Add a package under packages\n3. Code\n4. Use [ava](https://github.com/avajs/ava) for tests and [xo](https://github.com/sindresorhus/xo) for linting\n5. Submit a pull request\n\n## Prior art / inspirations / alternatives\n\n### Fetch-compatible\n\n- [fetch-plus](https://github.com/RickWong/fetch-plus)\n- [fetch-it](https://github.com/tryolabs/fetch-it)\n\n### Other\n\n- [axios](https://github.com/mzabriskie/axios)\n- [got](https://github.com/sindresorhus/got)\n- [request](https://github.com/request/request)\n- [reqwest](https://github.com/ded/reqwest)\n- [superagent](https://github.com/visionmedia/superagent)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffutpib%2Ffetish","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffutpib%2Ffetish","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffutpib%2Ffetish/lists"}