{"id":17444671,"url":"https://github.com/kleinfreund/retrieve","last_synced_at":"2026-02-27T20:02:36.940Z","repository":{"id":181609132,"uuid":"655865283","full_name":"kleinfreund/retrieve","owner":"kleinfreund","description":"Convenience wrapper around fetch","archived":false,"fork":false,"pushed_at":"2025-05-01T17:52:17.000Z","size":1781,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-30T18:18:52.570Z","etag":null,"topics":["fetch","http","request","wrapper"],"latest_commit_sha":null,"homepage":"https://retrieve.netlify.app","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/kleinfreund.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2023-06-19T19:14:08.000Z","updated_at":"2025-05-01T17:52:21.000Z","dependencies_parsed_at":"2024-10-20T15:52:05.991Z","dependency_job_id":null,"html_url":"https://github.com/kleinfreund/retrieve","commit_stats":null,"previous_names":["kleinfreund/retrieve"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/kleinfreund/retrieve","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kleinfreund%2Fretrieve","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kleinfreund%2Fretrieve/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kleinfreund%2Fretrieve/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kleinfreund%2Fretrieve/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kleinfreund","download_url":"https://codeload.github.com/kleinfreund/retrieve/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kleinfreund%2Fretrieve/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261632033,"owners_count":23187268,"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","request","wrapper"],"created_at":"2024-10-17T17:53:20.366Z","updated_at":"2026-02-27T20:02:31.908Z","avatar_url":"https://github.com/kleinfreund.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# retrieve\n\n[![Tests passing](https://github.com/kleinfreund/retrieve/workflows/Tests/badge.svg)](https://github.com/kleinfreund/retrieve/actions)\n\nA convenience wrapper around fetch for the browser (and anything that has `fetch` like Node.js).\n\nThis package’s files are distributed in the ES module format and have not been transpiled.\n\n**Links**:\n\n- [demo](https://retrieve.netlify.app)\n- [**npmjs.com**/package/retrieve](https://www.npmjs.com/package/retrieve)\n- [**github.com**/kleinfreund/retrieve](https://github.com/kleinfreund/retrieve)\n\t- [code of conduct](https://github.com/kleinfreund/retrieve/blob/main/CODE_OF_CONDUCT.md)\n\t- [contributing guidelines](https://github.com/kleinfreund/retrieve/blob/main/CONTRIBUTING.md)\n\n**Features** (see [Features](#features) for more detailed descriptions):\n\n- [sets the right “content-type” header based on the request body format](#request-content-type-guessing)\n- [serializes request bodies (JSON)](#request-body-serialization)\n- [deserializes response bodies (JSON, FormData, text)](#response-body-deserialization)\n- [allows typing deserialization result for successful and failed responses](#typing-response-data)\n- [supports interceptors that can implement error correcting logic](#interceptors)\n\nWhy is it called `retrieve`? I wanted to call it `makeRequest` (I like clean and explicit names), but that already exists on npm. So I went with `retrieve` because that's similar to `fetch`.\n\n## Contents\n\n- [Installation \u0026 usage](#installation--usage)\n\t- [As npm package](#as-npm-package)\n\t- [As plain JS file](#as-plain-js-file)\n- [Documentation](#documentation)\n\t- [Parameters](#parameters)\n\t\t- [`config`](#config)\n\t\t\t- [`config.url`](#configurl-required)\n\t\t\t- [`config.baseUrl`](#configbaseurl-optional)\n\t\t\t- [`config.params`](#configparams-optional)\n\t\t\t- [`config.init`](#configinit-optional)\n\t\t\t- [`config.data`](#configdata-optional)\n\t\t\t- [`config.timeout`](#configtimeout-optional)\n\t\t\t- [`config.beforeRequestHandlers`](#configbeforerequesthandlers-optional)\n\t\t\t- [`config.requestErrorHandlers`](#configrequesterrorhandlers-optional)\n\t\t\t- [`config.responseSuccessHandlers`](#configresponsesuccesshandlers-optional)\n\t\t\t- [`config.responseErrorHandlers`](#configresponseerrorhandlers-optional)\n\t\t- [`request`](#request)\n\t- [Return value](#return-value)\n\t- [Exceptions](#exceptions)\n\t\t- [`TypeError`](#typeerror)\n- [Examples](#examples)\n\t- [Example 1: make simple API request](#example-1-make-simple-api-request)\n\t- [Example 2: use response error](#example-2-use-response-error)\n\t- [Example 3: retrying requests](#example-3-retrying-requests)\n\t- [Example 4: submitting form data (POST)](#example-4-submitting-form-data-post)\n\t- [Example 5: submitting form data (GET)](#example-5-submitting-form-data-get)\n- [Features](#Features)\n\t- [Request content type guessing](#request-content-type-guessing)\n\t- [Request body serialization](#request-body-serialization)\n\t- [Response body deserialization](#response-body-deserialization)\n\t- [Typing response data](#typing-response-data)\n\t- [Interceptors](#interceptors)\n- [Versioning](#versioning)\n\n## Installation \u0026 usage\n\n### As npm package\n\nInstall the `retrieve` package.\n\n```sh\nnpm install retrieve\n```\n\nImport the `retrieve` function and use it.\n\n```js\nimport { retrieve } from 'retrieve'\n\nconst { data, response } = await retrieve({ url: 'http://example.org' })\nconsole.dir(data, response)\n```\n\n### As plain JS file\n\nDownload the `retrieve` module.\n\n```sh\ncurl -O 'https://cdn.jsdelivr.net/npm/retrieve@latest/dist/retrieve.js'\n```\n\nImport the `retrieve` function and use it.\n\n```html\n\u003cscript type=\"module\"\u003e\n\timport { retrieve } from './retrieve.js'\n\n\tconst { data, response } = await retrieve({ url: 'http://example.org' })\n\tconsole.dir(data, response)\n\u003c/script\u003e\n```\n\n## Documentation\n\nBasic usage of `retrieve` looks like this:\n\n```ts\nconst { data } = await retrieve({\n\turl: 'https://pokeapi.co/api/v2/pokemon',\n})\n```\n\nWith error handling and types:\n\n```ts\ntype Pokemon = { id: number, name: string }\ntype ApiError = string\n\nconst result = await retrieve\u003cPokemon, ApiError\u003e({\n\turl: 'https://pokeapi.co/api/v2/pokemon/25',\n})\n\nif (result instanceof ResponseError) {\n\tconsole.error(result.data)\n\t//                   ^^^^ ApiError\n} else {\n\tconsole.dir(result.data)\n\t//                 ^^^^ Pokemon\n}\n```\n\n### Parameters\n\nThe `retrieve` function takes one parameter, `configOrRequest`, which can be either a `RetrieveConfig` or `Request` object.\n\n#### `config`\n\nA `RetrieveConfig` object. Read the following sections to learn more.\n\n##### `config.url` (required)\n\nThe request URL.\n\n- `URL`: Will be used as-is.\n- `string`:\n\t- Absolute URL string: Will be used as-is.\n\t- Relative URL path string: Will be turned into an absolute URL (using `config.baseUrl`).\n\n**Note**: Providing a `Request` object to `config.url` is intentionally not possible. If you want to use a `Request` object, provide it _instead_ of `configOrRequest` (see [request](#request)).\n\n##### `config.baseUrl` (optional)\n\n**Default**: `window.location.origin` in browser environments; otherwise, `undefined`\n\nBase for request URL. Ignored if `config.url` is a `URL` object or an absolute URL `string`.\n\n##### `config.params` (optional)\n\nRequest query parameters. Will be appended to the request URL. Parameters already existing on the request URL will be overridden. New parameters will be added.\n\nFormData is intentionally not supported because it cannot be easily and reliably turned into an `URLSearchParams` object. If you can guarantee that your `FormData` object doesn't hold files, you can provide `config.params` using `new URLSearchParams(formData)`.\n\n##### `config.init` (optional)\n\nInit object passed to `fetch`.\n\nThe following changes are made to the `init` object before it is passed to `fetch` (but without changing `config.init`):\n\n- **Headers**: If no “content-type” header is set, it is determined automatically where appropriate:\n\n\t- “application/octet-stream” if `config.data` is an ArrayBuffer of Blob object\n\t- “text/plain” if `config.data` is a string\n\t- “application/json” if `config.data` is set and the request method isn't GET or HEAD\n\n\tNote, that if `config.data` is set to a `FormData` object, an existing content type **will be removed**. Read the warning on [MDN: Using FormData Objects: Sending files using a FormData object](https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects#sending_files_using_a_formdata_object) for an explanation.\n- **Body**: If `config.data` is set, it will be used for fetch's `init.body`. See `config.data` description for more information. Otherwise, if `config.init.body` is set, it will be used for fetch's `init.body`.\n- **Signal**: If `config.timeout` is set to a positive number, it will be used to create fetch's `init.signal` using `AbortSignal.timeout(config.timeout)`.\n\n##### `config.data` (optional)\n\nRequest body data.\n\nIf `config.data` is set:\n\n- … and the “content-type” header is “application/json”, `init.body` is set to the result of `JSON.stringify(config.data)`\n- … otherwise, `init.body` is set to `config.data`. It's your responsibility to make sure `config.data` can be used on `init.body` (see [fetch() global function: parameters](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters)).\n\n##### `config.timeout` (optional)\n\n**Default**: `0` (no timeout)\n\nRequest timeout in milliseconds.\n\n##### `config.beforeRequestHandlers` (optional)\n\nRun right before a request is sent (i.e. before calling `fetch`). Allows making changes to the parameters passed to `fetch` after they've been processed by `retrieve`. Also allows skipping the call to `fetch` entirely.\n\nExceptions during the processing of a before request handler are not caught.\n\nA before request handler can have one of two results:\n\n- proceeding with the execution of `retrieve` like normal\n- altering the normal execution of `retrieve` to avoid making the call to `fetch` entirely (indicated by returning a `Response` object)\n\nWhen returning a `Response` object in a before request handler, the call to `fetch` is skipped entirely and the subsequent execution of `retrieve` uses the `Response` object for all further logic.\n\n**Example**:\n\n```js\nconst config = {\n\turl: 'https://api.example.org',\n\tbeforeRequestHandlers: [\n\t\t(request) =\u003e {\n\t\t\trequest.headers.set('Authorization', 'Bearer ...')\n\t\t},\n\t],\n}\n```\n\n##### `config.requestErrorHandlers` (optional)\n\nRun when sending the request failed (i.e. the promise returned by `fetch` was rejected). Allows implementing corrective measures.\n\nExceptions during the processing of a request error handler are not caught.\n\nA request error handler can have one of two results:\n\n- maintaining the error state of the request\n- correcting the error state of the request (indicated by returning a `Response` object)\n\nReturning a `Response` object allows `retrieve` to continue processing the request as if no error occurred in the first place. Then, no further error request handlers will be processed.\n\n**Example**:\n\n```js\nconst config = {\n\turl: 'https://api.example.org',\n\trequestErrorHandlers: [\n\t\tasync (error, request) =\u003e {\n\t\t\t// Do something to fix the error cause\n\t\t\treturn await fetch(request)\n\t\t},\n\t],\n}\n```\n\nReturning an `Error` object (or not returning anything or returning `undefined`) makes `retrieve` continue treating the request as having errored. All request error handlers will be processed as long as the previous handlers maintain the error state (i.e. don't return a `Response` object). A returned `Error` object will be passed to subsequent handlers.\n\n**Example**:\n\n```js\nconst config = {\n\turl: 'https://api.example.org',\n\trequestErrorHandlers: [\n\t\t(error, request) =\u003e {\n\t\t\t// Do something with error\n\t\t\terror.message = 'ERR: ' + error.message\n\t\t\treturn error\n\t\t},\n\t],\n}\n```\n\n##### `config.responseSuccessHandlers` (optional)\n\nRun when sending the request succeeded and a response with a status code 200–299 was returned (i.e. the promise returned by `fetch` is fulfilled and yields a `Response` object whose `ok` property is set to `true`).\n\nExceptions during the processing of a response success handler are not caught.\n\n**Example**:\n\n```js\nconst config = {\n\turl: 'https://api.example.org',\n\tresponseSuccessHandlers: [\n\t\tasync (retrieveResponse) =\u003e {\n\t\t\t// Do something with retrieveResponse\n\t\t},\n\t],\n}\n```\n\n##### `config.responseErrorHandlers` (optional)\n\nRun when sending the request succeeded and a response with a status code \u003e=300 was returned (i.e. the promise returned by `fetch` is fulfilled and yields a `Response` object whose `ok` property is set to `false`).\n\nExceptions during the processing of a response error handler are not caught.\n\nA response error handler can have one of two results:\n\n- maintaining the error state of the response\n- correcting the error state of the response (indicated by returning a `Response` object)\n\nReturning a `Response` object allows `retrieve` to continue processing the response as if no error occurred in the first place. Then, no further error response handlers will be processed.\n\n**Example**:\n\n```js\nconst config = {\n\turl: 'https://api.example.org',\n\tresponseErrorHandlers: [\n\t\tasync (error, { request, response }) =\u003e {\n\t\t\tif (response.status === 401) {\n\t\t\t\t// Do something to fix the error cause (e.g. refresh the user's session)\n\t\t\t\tconst newAccessToken = '...'\n\t\t\t\trequest.headers.set('Authorization', `Bearer ${newAccessToken}`)\n\n\t\t\t\treturn await retrieve(request)\n\t\t\t}\n\t\t},\n\t],\n}\n```\n\nReturning an `Error` object makes `retrieve` continue treating the response as having errored. Note also that all response error handlers will be processed as long as the previous handlers maintain the error state (i.e. don't return a `Response` object).\n\n**Example**:\n\n```js\nconst config = {\n\turl: 'https://api.example.org',\n\tresponseErrorHandlers: [\n\t\tasync (error, retrieveResponse) =\u003e {\n\t\t\t// Do something with error\n\t\t\terror.message = 'ERR: ' + error.message\n\n\t\t\treturn error\n\t\t},\n\t],\n}\n```\n\n#### `request`\n\nWhen providing a `Request` object (instead of a `RetrieveConfig` object) to `retrieve`, all of retrieve's preprocessing steps are skipped and no interceptors are run. The `Request` object is instead directly passed to `fetch`. This is useful when retrying requests in `config.responseErrorHandlers`, for example.\n\n### Return value\n\nA `Promise` that fulfills with a `RetrieveResponse` object for successful responses (status code 200–299) or a `ResponseError` object for failed responses (status code \u003e=300).\n\nA `ResponseError` has access to:\n\n- `request`: the `Request` object used when calling `fetch`\n- `response`: the `Response` object returned by `fetch`\n- `data`: the deserialized response body contents\n\n```js\nconst result = await retrieve({\n\turl: 'https://pokeapi.co/api/v2/pokemon/grogu/',\n})\nif (result instanceof ResponseError) {\n\tconsole.error(result.response, result.data)\n} else {\n\tconsole.log(result.response, result.data)\n}\n```\n\n### Exceptions\n\n#### `TypeError`\n\nA `TypeError` is thrown when `fetch` does (see [fetch() global function: Exceptions](https://developer.mozilla.org/en-US/docs/Web/API/fetch#exceptions)).\n\n## Examples\n\n### Example 1: make simple API request\n\n```js\nasync function example() {\n\tconst { data, response } = await retrieve({\n\t\turl: 'https://pokeapi.co/api/v2/pokemon/pikachu/',\n\t})\n\tconsole.dir(data, response)\n}\n\nexample()\n```\n\n### Example 2: use response error\n\n```js\nasync function example() {\n\tconst result = await retrieve({\n\t\turl: 'https://pokeapi.co/api/v2/pokemon/grogu/',\n\t})\n\tconsole.dir(result)\n\tif (result instanceof ResponseError) {\n\t\tconsole.log(result.data)\n\t}\n}\n\nexample()\n```\n\n### Example 3: retrying requests\n\n```js\nasync function example() {\n\tawait retrieve({\n\t\turl: 'http://api.example.org/status',\n\t\tresponseErrorHandlers: [\n\t\t\tasync (error, { request, response }) =\u003e {\n\t\t\t\tif (response.status === 401) {\n\t\t\t\t\t// Do something to fix the error cause (e.g. refresh the user's session)\n\t\t\t\t\tconst newAccessToken = '...'\n\t\t\t\t\trequest.headers.set('Authorization', `Bearer ${newAccessToken}`)\n\n\t\t\t\t\treturn await retrieve(request)\n\t\t\t\t}\n\t\t\t},\n\t\t],\n\t})\n}\n\nexample()\n```\n\n### Example 4: submitting form data (POST)\n\n**Warning**: This is an educational example only. As it stands, a plain HTML `form` element without any JavaScript will handle such a use case just fine and do a better job of it. No need for `retrieve`.\n\n```html\n\u003cform method=\"POST\" enctype=\"multipart/form-data\"\u003e\n\t\u003clabel\u003e\n\t\tName\n\t\t\u003cinput type=\"text\" name=\"name\" value=\"value\"\u003e\n\t\u003c/label\u003e\n\n\t\u003clabel\u003e\n\t\tAge\n\t\t\u003cinput type=\"number\" name=\"age\" value=\"0\"\u003e\n\t\u003c/label\u003e\n\n\t\u003clabel\u003e\n\t\tFile\n\t\t\u003cinput type=\"file\" name=\"file\"\u003e\n\t\u003c/label\u003e\n\n\t\u003cbutton\u003eSubmit\u003c/button\u003e\n\u003c/form\u003e\n```\n\n```js\nconst form = document.querySelector('form')\n\nform.addEventListener('submit', function (event) {\n\tevent.preventDefault()\n\n\tconst form = event.target\n\n\tretrieve({\n\t\turl: form.action,\n\t\tdata: new FormData(form),\n\t\tinit: {\n\t\t\tmethod: form.method,\n\t\t},\n\t})\n})\n```\n\n### Example 5: submitting form data (GET)\n\n**Warning**: This is an educational example only. As it stands, a plain HTML `form` element without any JavaScript will handle such a use case just fine and do a better job of it. No need for `retrieve`.\n\n```html\n\u003cform\u003e\n\t\u003clabel\u003e\n\t\tName\n\t\t\u003cinput type=\"text\" name=\"name\" value=\"value\"\u003e\n\t\u003c/label\u003e\n\n\t\u003clabel\u003e\n\t\tAge\n\t\t\u003cinput type=\"number\" name=\"age\" value=\"0\"\u003e\n\t\u003c/label\u003e\n\n\t\u003cbutton\u003eSubmit\u003c/button\u003e\n\u003c/form\u003e\n```\n\n```js\nconst form = document.querySelector('form')\n\nform.addEventListener('submit', function (event) {\n\tevent.preventDefault()\n\n\tconst form = event.target\n\n\tretrieve({\n\t\turl: form.action,\n\t\tparams: new URLSearchParams(new FormData(form)),\n\t\tinit: {\n\t\t\tmethod: form.method,\n\t\t},\n\t})\n})\n```\n\n## Features\n\n### Request content type guessing\n\nThe content type header for the request is guessed based on the request body format (if a content type header isn't set already).\n\n- `application/octet-stream` if `config.data` is an `ArrayBuffer` or `Blob` object\n- `text/plain` if `config.data` is a string\n- `application/json` if `config.data` is set and the request method isn't GET or HEAD\n\n### Request body serialization\n\nThe request body is automatically serialized for JSON request bodies.\n\n### Response body deserialization\n\nThe response body is automatically deserialized for JSON, `FormData`, or text response bodies based on the response's content-type header. The deserialization happens on a _cloned_ `Response` object so that the body of the `Response` object included in `RetrieveResponse` and `ResponseError` objects can be consumed again.\n\n### Typing response data\n\nWhen using TypeScript, types for the deserialized response body can be provided via type parameters when calling `retrieve`. Two type parameters can be provided: one for the data if the response was successful (status code 200-299); one for the data if the response wasn't successful (status code \u003e=300)\n\n### Interceptors\n\nFour types of interceptors are supported:\n\n- Before request: processed before a request is sent\n- Request error: processed if a network error is encountered\n- Response success: processed if a response with status 200-299 is returned\n- Response error: processed if a response with status \u003e=300 is returned\n\nBoth error interceptors support error correcting logic triggered by returning a new `Response` object (e.g. the result of a new `fetch` call).\n\nSee [Example: retrying requests](#example-3-retrying-requests)\n\n## Versioning\n\nThis package uses [semantic versioning](https://semver.org).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkleinfreund%2Fretrieve","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkleinfreund%2Fretrieve","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkleinfreund%2Fretrieve/lists"}