{"id":13527447,"url":"https://github.com/groupon/gofer","last_synced_at":"2025-05-08T21:38:16.942Z","repository":{"id":18513482,"uuid":"21710399","full_name":"groupon/gofer","owner":"groupon","description":"A general purpose service client library for node.js","archived":true,"fork":false,"pushed_at":"2024-02-12T21:52:55.000Z","size":1758,"stargazers_count":82,"open_issues_count":8,"forks_count":18,"subscribers_count":14,"default_branch":"main","last_synced_at":"2025-05-08T21:38:15.731Z","etag":null,"topics":["fetch","http-client","javascript","nodejs"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/groupon.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}},"created_at":"2014-07-10T21:08:31.000Z","updated_at":"2025-04-28T08:31:56.000Z","dependencies_parsed_at":"2024-06-18T17:37:09.399Z","dependency_job_id":"7f8566ca-3f70-49e0-a368-15b7d642d766","html_url":"https://github.com/groupon/gofer","commit_stats":null,"previous_names":[],"tags_count":100,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/groupon%2Fgofer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/groupon%2Fgofer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/groupon%2Fgofer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/groupon%2Fgofer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/groupon","download_url":"https://codeload.github.com/groupon/gofer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253153543,"owners_count":21862370,"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","http-client","javascript","nodejs"],"created_at":"2024-08-01T06:01:48.229Z","updated_at":"2025-05-08T21:38:16.910Z","avatar_url":"https://github.com/groupon.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"[![nlm-github](https://img.shields.io/badge/github-groupon%2Fgofer%2Fissues-F4D03F?logo=github\u0026logoColor=white)](https://github.com/groupon/gofer/issues)\n![nlm-node](https://img.shields.io/badge/node-%3E%3D10.13-blue?logo=node.js\u0026logoColor=white)\n![nlm-version](https://img.shields.io/badge/version-5.3.0-blue?logo=version\u0026logoColor=white)\n[![Build Status](https://travis-ci.com/groupon/gofer.svg?branch=main)](https://travis-ci.com/groupon/gofer)\n\n# `gofer`\n\n\u003e A gofer, go-fer or gopher /ˈɡoʊfər/ is an employee who specializes\n\u003e in delivery of special items to their superior(s).\n\u003e The special items may be anything from a cup of coffee to a tailored\n\u003e suit or a car.\n\u003e\n\u003e — \u003ccite\u003e[Wikipedia: Gofer](https://en.wikipedia.org/wiki/Gofer)\u003c/cite\u003e\n\n```\nnpm install --save gofer\n```\n\nA base class for HTTP clients.\nUsable in node, browsers, and react-native.\nThe design is meant to enforce a certain level of consistency in how the\nclients are configured and instrumented.\n\nUse in browsers might require a `fetch` polyfill.\n\nIf you used `gofer` 2.x before,\nyou might want to read about [all the changes in 3.x](/BREAKING_v3.md).\n\nIf you have some old `gofer` 3.x code that doesn't use classes and Promises,\nyou can read the [previous version of the 3.x docs][old-3x-docs].\n\n[old-3x-docs]: https://github.com/groupon/gofer/blob/v3.7.0/README.md\n\n**[API docs](/API.md)** •\n**[Walkthrough](#walkthrough)**\n\n### Features\n\n#### Options mappers\n\nOption mappers are called in the order they are\nregistered in and can potentially do *anything* they want.\nThis can range from applying defaults over resolving custom api options\nto injecting access tokens.\n\n\n#### Defaults merging\n\nAll configuration is just defaults which is one of the things making\noption mappers so powerful.\n\nThe precedence rules are (first wins):\n\n1. Explicit configuration in the API call\n2. Scoped overrides using `client.with(options)`\n3. Endpoint-level defaults\n4. Service-level defaults\n5. Global defaults\n\nSee the [walkthrough](#walkthrough) below for how these are configured.\n\n\n#### Copy with defaults / scoped overrides\n\nYou can create a copy of the API with hard defaults using `with`.\nThis enables a nice pattern:\n\n```js\n// We'll assume MyApiClient has an option mapper than knows how to\n// properly send an accessToken, e.g. using an Authentication header\nconst client = new MyApiClient(config);\n\n// After retrieving an access token\nconst authenticatedClient = client.with({ accessToken: 'some-token' });\n\n// This one will now send an access token\n// ~\u003e `curl -H 'Authentication: Bearer some-token' \\\n//          http://api.example.com/personal/some-id`\nauthenticatedClient.protectedResource('some-id');\n\n// This one was not changed, so it will not send one\n// ~\u003e `curl http://api.example.com/personal/some-id`\nclient.protectedResource('some-id');\n```\n\n\n### This sounds great, but...\n\n#### Why not use service specific client libraries?\n\nWell, in a way that's what `gofer` encourages.\nThe difference is that by basing all client libraries on this one,\nyou gain consistency and unified configuration.\nCreating a client for a new service often takes just a couple of lines.\n\n#### Why not use [`request`](https://github.com/request/request)?\n\n`request` is a great swiss army knive for making API calls.\nThis makes it an awesome first pick if you're looking for a quick way to\ntalk to a wide variety of 3rd party services.\nBut it's lacking in a few areas we care a lot about:\n\n* Good, predictable error handling\n* Flexible configuration\n* Instrumentation friendly\n\n\n## Walkthrough\n\nLet's say we need a client for the Github API.\nThe first step is to generate a Github client class:\n\n```js\nconst Gofer = require('gofer');\n\nconst { version, name } = require('./package.json')\n\nclass Github extends Gofer {\n  constructor(config) {\n    super(config, 'github', version, name);\n  }\n}\n```\n\nThe name you choose here (\"github\") determines which section of the\nconfiguration it will accept.\nIt's also part of the instrumentation as `serviceName`.\n\nLet's define a simple endpoint to get the emojis from Github:\n\n```js\nclass Github extends Gofer {\n  /* earlier stuff here */\n\n  // Every instance of Github will get an `emojis` method, which has access\n  // to `this.fetch()`  The `fetch` method works similar to WHATWG/fetch.\n  emojis() {\n    // fetch(uri: string, options: object?, callback: function?)\n    return this.fetch('/emojis');\n  }\n}\n```\n\nTo create an instance, we need to provide configuration.\nConfiguration exists on three levels: global, per-service, and per-endpoint.\n\n```js\nconst config = {\n  globalDefaults: {\n    // these apply to all gofers\n    connectTimeout: 30,\n    timeout: 100,\n  },\n  github: {\n    // these apply for every call made with Github\n    clientId: '\u003cVALID CLIENT ID HERE\u003e',\n    endpointDefaults: {\n      // these only apply for calls to the emojis endpoint\n      emojis: {\n        connectTimeout: 100,\n        timeout: 2000,\n      },\n    },\n  },\n};\n```\n\nTo make our client a little nicer to use we'll add an [option mapper](/API.md#option-mappers) that defaults `baseUrl` to the public Github API.\nThe options we return will be passed on to `fetch`.\n\n```js\nGithub.prototype.addOptionMapper(opts =\u003e {\n  // opts contains the already merged options, including global-, service-,\n  // and endpoint-defaults. In our example opts.timeout will be 2000, etc.\n  return {...opts,  baseUrl: 'https://api.github.com' };\n});\n```\n\nFinally we can instantiate and make the call:\n\n```js\nconst github = new Github(config);\n\n// The `fetch`-style:\ngithub.emojis()\n  .then(res =\u003e res.json())\n  .then(emojiList =\u003e {\n    console.log('Returned %d emojis', Object.keys(emojiList).length);\n  })\n  .catch(console.error);\n\n// Using the added convenience of req.json()\ngithub.emojis()\n  .json()\n  .then(emojiList =\u003e { /* ... same ... */ })\n  .catch(console.error);\n```\n\nYou can check `examples/github.js` for a richer example.\n\n## File Uploads\n\nGofer does not by default ship with support for `multipart/form-data` file\nuploads, but it is easy to add, using the [form-data][form-data] module,\nand an option mapper as shown in this [multi-part mapper test][multi-part].\n\n[form-data]: https://www.npmjs.com/package/form-data\n[multi-part]: test/multi-part-mapper.test.js\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgroupon%2Fgofer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgroupon%2Fgofer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgroupon%2Fgofer/lists"}