{"id":13605754,"url":"https://github.com/truework/gretchen","last_synced_at":"2025-04-12T05:34:28.457Z","repository":{"id":38174005,"uuid":"234645034","full_name":"truework/gretchen","owner":"truework","description":"Making fetch happen in TypeScript.","archived":false,"fork":false,"pushed_at":"2023-10-05T02:58:31.000Z","size":2836,"stargazers_count":329,"open_issues_count":17,"forks_count":5,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-10-31T22:02:42.537Z","etag":null,"topics":["fetch","http","http-client","micro","request","tiny","typescript"],"latest_commit_sha":null,"homepage":"https://www.truework.com/labs","language":"TypeScript","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/truework.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}},"created_at":"2020-01-17T22:07:51.000Z","updated_at":"2024-09-03T11:03:55.000Z","dependencies_parsed_at":"2023-02-08T19:46:00.893Z","dependency_job_id":"52cd9a1f-e893-4efa-8f2f-f0cafe813d04","html_url":"https://github.com/truework/gretchen","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truework%2Fgretchen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truework%2Fgretchen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truework%2Fgretchen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truework%2Fgretchen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/truework","download_url":"https://codeload.github.com/truework/gretchen/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223498020,"owners_count":17155242,"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","http-client","micro","request","tiny","typescript"],"created_at":"2024-08-01T19:01:02.351Z","updated_at":"2024-11-07T10:31:09.450Z","avatar_url":"https://github.com/truework.png","language":"TypeScript","readme":"# gretchen ![npm](https://img.shields.io/npm/v/gretchen) [![](https://badgen.net/bundlephobia/minzip/gretchen)](https://bundlephobia.com/result?p=gretchen)\n\nMaking `fetch` happen in TypeScript.\n\n\u003e Looking for more info? Check out [our blog post](https://medium.com/@estrattonbailey/introducing-gretchen-making-fetch-happen-in-typescript-87ab0bd66027?source=friends_link\u0026sk=884da87efacd2db29d670a04f6651f60).\n\n## Features\n\n- **safe:** will not throw on non-200 responses\n- **precise:** allows for typing of both success \u0026 error responses\n- **resilient:** configurable retries \u0026 timeout\n- **smart:** respects `Retry-After` header\n- **small:** won't break your bundle\n\n### Install\n\n```bash\nnpm i gretchen --save\n```\n\n### Browser support\n\n`gretchen` targets all modern browsers. For IE11 support, you'll need to polyfill\n`fetch`, `Promise`, and `Object.assign`. For Node.js, you'll need `fetch` and\n`AbortController`.\n\n### Quick links\n\n- [Usage](#usage)\n- [Making a request](#making-a-request)\n  - [Options](#options)\n  - [Retrying requests](#retrying-requests)\n  - [Timeouts](#timeouts)\n- [Response handling](#response-handling)\n- [Hooks](#hooks)\n- [Creating instances](#creating-instances)\n- [Usage with TypeScript](#usage-with-typescript)\n- [Why?](#why)\n  - [Credits](#credits)\n  - [License](#license)\n\n# Usage\n\nWith `fetch`, you might do something like this:\n\n```js\nconst request = await fetch(\"/api/user/12\");\nconst user = await request.json();\n```\n\nWith `gretchen`, it's very similar:\n\n```js\nimport { gretch } from \"gretchen\";\n\nconst { data: user } = await gretch(\"/api/user/12\").json();\n```\n\n👉 `gretchen` aims to provide just enough abstraction to provide ease of use\nwithout sacrificing flexibility.\n\n## Making a request\n\nUsing `gretchen` is very similar to using `fetch`. It too defaults to `GET`, and\nsets the `credentials` header to `same-origin`.\n\n```js\nconst request = gretch(\"/api/user/12\");\n```\n\nTo parse a response body, simply call any of the standard `fetch` [body interface\nmethods](https://developer.mozilla.org/en-US/docs/Web/API/Response#Body_Interface_Methods_2):\n\n```js\nconst response = await request.json();\n```\n\nThe slight diversion from native `fetch` here is to allow users to do this in\none shot:\n\n```js\nconst response = await gretch(\"/api/user/12\").json();\n```\n\nIn addition to the _body interface methods_ you're familiar with, there's also a\n`flush()` method. This resolves the request _without_ parsing the body (or\nerrors), which results in slight performance gains. This method returns a\nslightly different response object, see below for more details.\n\n```js\nconst response = await gretch(\"/api/user/authenticated\").flush();\n```\n\n### Options\n\nTo make different types of requests or edit headers and other request config,\npass a options object:\n\n```js\nconst response = await gretch(\"/api/user/12\", {\n  credentials: \"include\",\n  headers: {\n    \"Tracking-ID\": \"abcde12345\",\n  },\n}).json();\n```\n\nConfiguring requests bodies should look familiar as well:\n\n```js\nconst response = await gretch(\"/api/user/12\", {\n  method: \"PATCH\",\n  body: JSON.stringify({\n    name: \"Megan Rapinoe\",\n    occupation: \"President of the United States\",\n  }),\n}).json();\n```\n\nFor convenience, there’s also a `json` shorthand. We’ll take care of\nstringifying the body and applying the `Content-Type` header:\n\n```js\nconst response = await gretch(\"/api/user/12\", {\n  method: \"PATCH\",\n  json: {\n    email: \"m.rapinoe@gmail.com\",\n  },\n}).json();\n```\n\n#### Retrying requests\n\n`gretchen` will automatically attempt to retry _some_ types of requests if they\nreturn certain error codes. Below are the configurable options and their\ndefaults:\n\n- `attempts` - a `number` of retries to attempt before failing. Defaults to `2`.\n- `codes` - an `array` of `number` status codes that indicate a retry-able\n  request. Defaults to `[ 408, 413, 429 ]`.\n- `methods` - an `array` of `string`s indicating which request methods should be\n  retry-able. Defaults to `[ \"GET\" ]`.\n- `delay` - a `number` in milliseconds used to exponentially back-off the delay\n  time between requests. Defaults to `6`. Example: first delay is 6ms, second\n  36ms, third 216ms, and so on.\n\nThese options can be set using the configuration object:\n\n```js\nconst response = await gretch(\"/api/user/12\", {\n  retry: {\n    attempts: 3,\n  },\n}).json();\n```\n\n#### Timeouts\n\nBy default, `gretchen` will time out requests after 10 seconds and retry them, unless otherwise configured. To configure timeout, pass a value in milliseconds:\n\n```js\nconst response = await gretch(\"/api/user/12\", {\n  timeout: 20000,\n}).json();\n```\n\n## Response handling\n\n`gretchen`'s thin abstraction layer returns a specialized structure from a\nrequest. In TypeScript terms, it employs a _discriminated union_ for ease of\ntyping. More on that later.\n\n```js\nconst { url, status, error, data, response } = await gretch(\n  \"/api/user/12\"\n).json();\n```\n\n`url` and `status` here are what they say they are: properties of the `Response`\nreturned from the request.\n\n#### `data`\n\nIf the response returns a `body` and you elect to parse it i.e. `.json()`, it\nwill be populated here.\n\n#### `error`\n\nAnd instead of throwing errors `gretchen` will populate the `error` prop with\nany errors that occur **_or_** `body`ies returned from non-success (`4xx`)\nresponses.\n\nExamples of `error` usage:\n\n- a `/login` endpoint returns `401` and includes a message for the user\n- an endpoint times out and an `HTTPTimeout` error is returned\n- an unknown network error occurs during the request\n\n#### `response`\n\n`gretchen` also provides the full `response` object in case you need it.\n\n#### Usage with `flush`\n\nAs mentioned above, `gretchen` also provides a `flush()` method to resolve a\nrequest without parsing the body or errors. This results in a slightly different\nresponse object.\n\n```js\nconst { url, status, response } = await gretch(\n  \"/api/user/authenticated\"\n).flush();\n```\n\n## Hooks\n\n`gretchen` uses the concept of \"hooks\" to tap into the request lifecycle. Hooks\nare good for code that needs to run on every request, like adding tracking\nheaders and logging errors.\n\nHooks should be defined as an array. That way you can compose multiple hooks\nper-request, and define and merge default hooks when [creating\ninstances](#creating-instances).\n\n#### `before`\n\nThe `before` hook runs just prior to the request being made. You can even modify\nthe request directly, like to add headers. The `before` hook is passed the `Request`\nobject, and the full options object.\n\n```js\nconst response = await gretch(\"/api/user/12\", {\n  hooks: {\n    before: [\n      (request, options) =\u003e {\n        request.headers.set(\"Tracking-ID\", \"abcde\");\n      },\n    ],\n  },\n}).json();\n```\n\n#### `after`\n\nThe `after` runs after the request has resolved and any body interface methods\nhave been called. It has the opportunity to read the `gretchen` response. It\n_cannot_ modify it. This is mostly useful for logging.\n\n```js\nconst response = await gretch(\"/api/user/12\", {\n  hooks: {\n    after: [\n      ({ url, status, data, error }, options) =\u003e {\n        sentry.captureMessage(`${url} returned ${status}`);\n      },\n    ],\n  },\n}).json();\n```\n\n## Creating instances\n\n`gretchen` also exports a `create` method that allows you to configure default\noptions. This is useful if you want to attach something like logging to every\nrequest made with the returned instance.\n\n```js\nimport { create } from \"gretchen\";\n\nconst gretch = create({\n  headers: {\n    \"X-Powered-By\": \"gretchen\",\n  },\n  hooks: {\n    after({ error }) {\n      if (error) sentry.captureException(error);\n    },\n  },\n});\n\nawait gretch(\"/api/user/12\").json();\n```\n\n### Base URLs\n\nAnother common use case for creating a separate instance is to specify a\n`baseURL` for all requests. The `baseURL` will then be resolved against the base\nURL of the page, allowing support for both absolute and relative `baseURL`\nvalues.\n\nIn the example below, assume requests are being made from a page located at\n`https://www.mysite.com`.\n\nFunctionally, this:\n\n```js\nconst gretch = create({\n  baseURL: \"https://www.mysite.com/api\",\n});\n```\n\nIs equivalent to this:\n\n```js\nconst gretch = create({\n  baseURL: \"/api\",\n});\n```\n\nSo this request:\n\n```js\nawait gretch(\"/user/12\").json();\n```\n\nWill resolve to `https://www.mysite.com/api/user/12`.\n\n**Note:** if a `baseURL` is specified, URLs will be normalized in order to\nconcatenate them i.e. a leading slash – `/user/12` vs `user/12` – will not\nimpact how the request is resolved.\n\n## Usage with TypeScript\n\n`gretchen` is written in TypeScript and employs a _discriminated union_ to allow\nyou to type and consume both the success and error responses returned by your\nAPI.\n\nTo do so, pass your data types directly to the `gretch` call:\n\n```typescript\ntype Success = {\n  name: string;\n  occupation: string;\n};\n\ntype Error = {\n  code: number;\n  errors: string[];\n};\n\nconst response = await gretch\u003cSuccess, Error\u003e(\"/api/user/12\").json();\n```\n\nThen, you can safely use the responses:\n\n```typescript\nif (response.error) {\n  const {\n    code, // number\n    errors, // array of strings\n  } = response.error; // typeof Error\n} else if (response.data) {\n  const {\n    name, // string\n    occupation, // string\n  } = response.data; // typeof Success\n}\n```\n\n# Why?\n\nThere are a lot of options out there for requesting data. But most modern\n`fetch` implementations rely on throwing errors. For type-safety, we wanted\nsomething that would allow us to type the response, no matter what. We also\nwanted to bake in a few opinions of our own, although the API is flexible enough\nfor most other applications.\n\n### Credits\n\nThis library was inspired by [ky](https://github.com/sindresorhus/ky), [fetch-retry](https://github.com/zeit/fetch-retry), and others.\n\n### License\n\nMIT License © [Truework](https://truework.com)\n\n\u003cbr /\u003e\n\n![cheap movie reference](https://user-images.githubusercontent.com/4732330/73581652-928c6100-444f-11ea-8796-7cdc77271d06.png)\n","funding_links":[],"categories":["TypeScript","API Layer"],"sub_categories":["Reactive Programming"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftruework%2Fgretchen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftruework%2Fgretchen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftruework%2Fgretchen/lists"}