{"id":15031085,"url":"https://github.com/posva/mande","last_synced_at":"2025-05-14T00:10:17.593Z","repository":{"id":36979581,"uuid":"264743170","full_name":"posva/mande","owner":"posva","description":"\u003c700 bytes convenient and modern wrapper around fetch with smart extensible defaults","archived":false,"fork":false,"pushed_at":"2025-05-05T06:58:17.000Z","size":3323,"stargazers_count":1273,"open_issues_count":5,"forks_count":43,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-05-05T07:42:17.347Z","etag":null,"topics":["api","axios","fetch","promise"],"latest_commit_sha":null,"homepage":"https://mande.esm.is","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/posva.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":".github/funding.yml","license":"LICENSE","code_of_conduct":".github/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},"funding":{"github":"posva"}},"created_at":"2020-05-17T19:40:26.000Z","updated_at":"2025-05-05T06:58:19.000Z","dependencies_parsed_at":"2023-02-15T15:01:33.954Z","dependency_job_id":"ba8f06a9-144f-4a60-b034-c92030ed019b","html_url":"https://github.com/posva/mande","commit_stats":{"total_commits":581,"total_committers":10,"mean_commits":58.1,"dds":0.6247848537005163,"last_synced_commit":"2a3ebebe87d457bfcf915568e430ab818ba70e4d"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/posva%2Fmande","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/posva%2Fmande/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/posva%2Fmande/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/posva%2Fmande/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/posva","download_url":"https://codeload.github.com/posva/mande/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252463271,"owners_count":21751757,"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":["api","axios","fetch","promise"],"created_at":"2024-09-24T20:14:52.388Z","updated_at":"2025-05-14T00:10:12.566Z","avatar_url":"https://github.com/posva.png","language":"TypeScript","readme":"# mande [![ci](https://github.com/posva/mande/actions/workflows/ci.yml/badge.svg)](https://github.com/posva/mande/actions/workflows/ci.yml) [![npm package](https://badgen.net/npm/v/mande)](https://www.npmjs.com/package/mande) [![codecov](https://codecov.io/github/posva/mande/graph/badge.svg?token=ItUkHTdc2q)](https://codecov.io/github/posva/mande) [![thanks](https://badgen.net/badge/thanks/♥/pink)](https://github.com/posva/thanks)\n\n\u003e Simple, light and extensible wrapper around fetch with smart defaults\n\n**Requires [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) support.**\n\n_mande_ has better defaults to communicate with APIs using `fetch`, so instead of writing:\n\n```js\n// creating a new user\nfetch('/api/users', {\n  method: 'POST',\n  headers: {\n    Accept: 'application/json',\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    name: 'Dio',\n    password: 'irejectmyhumanityjojo',\n  }),\n})\n  .then((response) =\u003e {\n    if (response.status \u003e= 200 \u0026\u0026 response.status \u003c 300) {\n      return response.json()\n    }\n    // reject if the response is not 2xx\n    throw new Error(response.statusText)\n  })\n  .then((user) =\u003e {\n    // ...\n  })\n```\n\nYou can write:\n\n```js\nconst users = mande('/api/users')\n\nusers\n  .post({\n    name: 'Dio',\n    password: 'irejectmyhumanityjojo',\n  })\n  .then((user) =\u003e {\n    // ...\n  })\n```\n\n## Installation\n\n```sh\nnpm install mande\nyarn add mande\n```\n\n## Usage\n\nCreating a small layer to communicate to your API:\n\n```js\n// api/users\nimport { mande } from 'mande'\n\nconst users = mande('/api/users', usersApiOptions)\n\nexport function getUserById(id) {\n  return users.get(id)\n}\n\nexport function createUser(userData) {\n  return users.post(userData)\n}\n```\n\nAdding _Authorization_ tokens:\n\n```js\n// api/users\nimport { mande } from 'mande'\n\nconst todos = mande('/api/todos', todosApiOptions)\n\nexport function setToken(token) {\n  // todos.options will be used for all requests\n  todos.options.headers.Authorization = 'Bearer ' + token\n}\n\nexport function clearToken() {\n  delete todos.options.headers.Authorization\n}\n\nexport function createTodo(todoData) {\n  return todo.post(todoData)\n}\n```\n\n```js\n// In a different file, setting the token whenever the login status changes. This depends on your frontend code, for instance, some libraries like Firebase provide this kind of callback but you could use a watcher on Vue.\nonAuthChange((user) =\u003e {\n  if (user) setToken(user.token)\n  else clearToken()\n})\n```\n\nYou can also globally add default options to all _mande_ instances:\n\n```js\nimport { defaults } from 'mande'\n\ndefaults.headers.Authorization = 'Bearer token'\n```\n\nTo delete a header, pass `null` to the mande instance or the request:\n\n```ts\nconst legacy = mande('/api/v1/data', {\n  headers: {\n    // override all requests\n    'Content-Type': 'application/xml',\n  },\n})\n\n// override only this request\nlegacy.post(new FormData(), {\n  headers: {\n    // overrides Accept: 'application/json' only for this request\n    Accept: null,\n    'Content-Type': null,\n  },\n})\n```\n\n## TypeScript\n\nAll methods defined on a `mande` instance accept a type generic to type their return:\n\n```ts\nconst todos = mande('/api/todos', globalOptions)\n\ntodos\n  .get\u003c{ text: string; id: number; isFinished: boolean }[]\u003e()\n  .then((todos) =\u003e {\n    // todos is correctly typed\n  })\n```\n\n## SSR (and Nuxt in Universal mode)\n\nTo make Mande work on Server, make sure to provide a `fetch` polyfill and to use full URLs and not absolute URLs starting with `/`. For example, using `node-fetch`, you can do:\n\n```js\nexport const BASE_URL = process.server\n  ? process.env.NODE_ENV !== 'production'\n    ? 'http://localhost:3000'\n    : 'https://example.com'\n  : // on client, do not add the domain, so urls end up like `/api/something`\n    ''\n\nconst fetchPolyfill = process.server ? require('node-fetch') : fetch\nconst contents = mande(BASE_URL + '/api', {}, fetchPolyfill)\n```\n\n### Nuxt 2\n\nNote: If you are doing SSR with authentication, Nuxt 3 hasn't been adapted yet. See #308.\n\nWhen using with Nuxt **and SSR**, you must wrap exported functions so they automatically proxy cookies and headers on the server:\n\n```js\nimport { mande, nuxtWrap } from 'mande'\nconst fetchPolyfill = process.server ? require('node-fetch') : fetch\nconst users = mande(BASE_URL + '/api/users', {}, fetchPolyfill)\n\nexport const getUserById = nuxtWrap(users, (api, id: string) =\u003e api.get(id))\n```\n\nMake sure to add it as a buildModule as well:\n\n```js\n// nuxt.config.js\nmodule.exports = {\n  buildModules: ['mande/nuxt'],\n}\n```\n\nThis prevents requests from accidentally sharing headers or bearer tokens.\n\n#### TypeScript\n\nMake sure to include `mande/nuxt` in your `tsconfig.json`:\n\n```json\n{\n  \"types\": [\"@types/node\", \"@nuxt/types\", \"mande/nuxt\"]\n}\n```\n\n## API\n\nMost of the code can be discovered through the autocompletion but the API documentation is available at [https://mande.esm.is](https://mande.esm.is)\n\n### Cookbook\n\n#### Timeout\n\nYou can timeout requests by using the native `AbortSignal`:\n\n```ts\nmande('/api').get('/users', { signal: AbortSignal.timeout(2000) })\n```\n\nThis is supported by [all modern browsers](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static).\n\n#### `FormData`\n\nWhen passing [Form Data](https://developer.mozilla.org/en-US/docs/Web/API/FormData), mande automatically removes the `Content-Type` header but you can manually set it if needed:\n\n```ts\n// directly pass it to the mande instance\nconst api = mande('/api/', { headers: { 'Content-Type': null } })\n// or when creating the request\nconst formData = new FormData()\napi.post(formData, {\n  headers: { 'Content-Type': 'multipart/form-data' },\n})\n```\n\nMost of the time you should let the browser set it for you.\n\n## Related\n\n- [fetchival](https://github.com/typicode/fetchival): part of the code was borrowed from it and the api is very similar\n- [axios](https://github.com/axios/axios):\n\n## License\n\n[MIT](http://opensource.org/licenses/MIT)\n","funding_links":["https://github.com/sponsors/posva"],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fposva%2Fmande","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fposva%2Fmande","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fposva%2Fmande/lists"}