{"id":30171942,"url":"https://github.com/eunjae-lee/apitool","last_synced_at":"2025-08-11T22:07:33.482Z","repository":{"id":40953944,"uuid":"137660704","full_name":"eunjae-lee/apitool","owner":"eunjae-lee","description":"An organized way to work with your APIs","archived":false,"fork":false,"pushed_at":"2023-01-04T05:51:18.000Z","size":1549,"stargazers_count":32,"open_issues_count":18,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-14T18:49:31.716Z","etag":null,"topics":["api","api-client","api-rest","api-wrapper","http","http-client","javascript","javascript-library"],"latest_commit_sha":null,"homepage":"","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/eunjae-lee.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-06-17T14:23:01.000Z","updated_at":"2023-08-14T09:44:22.000Z","dependencies_parsed_at":"2023-02-01T23:46:21.399Z","dependency_job_id":null,"html_url":"https://github.com/eunjae-lee/apitool","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/eunjae-lee/apitool","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eunjae-lee%2Fapitool","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eunjae-lee%2Fapitool/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eunjae-lee%2Fapitool/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eunjae-lee%2Fapitool/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eunjae-lee","download_url":"https://codeload.github.com/eunjae-lee/apitool/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eunjae-lee%2Fapitool/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269963898,"owners_count":24504452,"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","status":"online","status_checked_at":"2025-08-11T02:00:10.019Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","api-client","api-rest","api-wrapper","http","http-client","javascript","javascript-library"],"created_at":"2025-08-11T22:07:10.188Z","updated_at":"2025-08-11T22:07:33.469Z","avatar_url":"https://github.com/eunjae-lee.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003cimg width=\"256\" src=\"https://raw.githubusercontent.com/eunjae-lee/apitool/master/logo.png\"\u003e\u003c/p\u003e\n\n[![Build Status](https://travis-ci.org/eunjae-lee/apitool.svg?branch=master)](https://travis-ci.org/eunjae-lee/apitool)\n[![npm version](http://img.shields.io/npm/v/apitool.svg)](https://npmjs.org/package/apitool)\n[![GitHub stars](https://img.shields.io/github/stars/eunjae-lee/apitool.svg)](https://github.com/eunjae-lee/apitool/stargazers)\n[![Twitter](https://img.shields.io/twitter/url/https/github.com/eunjae-lee/apitool.svg?style=social)](https://twitter.com/intent/tweet?text=Wow:\u0026url=https%3A%2F%2Fgithub.com%2Feunjae-lee%2Fapitool)\n\n\n# Introduction\n\n`apitool` is a wrapper of `axios`. It provides an organized way to work with APIs.\n\nThis goes smoothly with your TypeScript-based project since it's written in TypeScript.\n\n# Table of contents\n\n\u003c!-- toc --\u003e\n\n- [Install](#install)\n- [Getting Started](#getting-started)\n  * [Performing a simple `GET` request](#performing-a-simple-get-request)\n  * [Performing a simple `POST` request](#performing-a-simple-post-request)\n  * [`result` Schema](#result-schema)\n- [Getting deeper](#getting-deeper)\n  * [`ErrorType` Schema](#errortype-schema)\n  * [Combining configs](#combining-configs)\n  * [Importing things](#importing-things)\n  * [Contributing](#contributing)\n  * [Author](#author)\n\n\u003c!-- tocstop --\u003e\n\n# Install\n\n```bash\nnpm install apitool --save\n```\n\n# Getting Started\n\n## Performing a simple `GET` request\n\n```js\nimport Api from 'apitool';\n\nconst result = await new Api().request(\"get\", url);\n// or\nconst result = await new Api().get(url);\n```\n\nYou can, of course, put params like the following:\n\n```js\nconst params = {\n  ...\n}\n\nconst result = await new Api().request(\"get\", url, params);\n// or\nconst result = await new Api().get(url, params);\n```\n\n## Performing a simple `POST` request\n\nAs you guess,\n\n```js\nconst data = {\n  ...\n}\n\nconst result = await new Api().request(\"post\", url, data);\n// or\nconst result = await new Api().post(url, data);\n```\n\n`apitool` provides `get`, `post`, `put` and `delete`.\n\n## `result` Schema\n\nThe request returns a `result`. It looks like the following:\n\n```js\n{\n  // It indicates if error has occurred\n  error: boolean;\n\n  // It indicates what kind of error has occurred\n  errorType?: ErrorType;\n\n  // It holds an error code or any error-related data\n  errorCode?: any;\n\n  // A response object which has been transformed by your transformers.\n  response?: T | undefined;\n\n  // An original axios response object\n  orgResponse?: AxiosResponse;\n}\n```\n\nSo after executing api call, you can handle error like this:\n\n```js\nconst result = await new Api().get(url);\nif (result.error) {\n  // handleError with result.errorType and result.errorCode\n} else {\n  // do something with result.response\n}\n```\n\nOr with object destructuring,\n\n```js\nconst { error, errorType, errorCode, response } = await new Api().get(url);\nif (error) {\n  // handleError\n} else {\n  // do something\n}\n```\n\n# Getting deeper\n\nSo far it doesn't seem to be different from `axios`. Here's an real life example to help you understand what `apitool` really exists for.\n\n```js\nconst myApi = new Api().extend({\n  baseURL: MY_DOMAIN,\n  before: [\n    () =\u003e showLoader()\n  ],\n  after: [\n    () =\u003e hideLoader()\n  ]\n  transformData: [\n    (data) =\u003e decamelizeKeys(data),\n  ],\n  transformResponse: [\n    (response) =\u003e camelizeKeys(response),\n  ]\n})\n```\n\nIn JavaScript people usually use camelCase and in rails or in some server-side languages they usually use snake_case. With `transformData` and `transformResponse`, you can convert cases easily. And unlike `axios`, `transformData` applies to all methods including `get`.\n\n`before` and `after` helps you execute things before request and things after request.\n\nWith `apitool`, you can extend this.\n\n```js\nconst myAuthApi = myApi.extend({\n  headers: {\n    Authorization: () =\u003e getAuthToken()\n  },\n  responseValidations: [\n    async (response, context, orgResponse) =\u003e {\n      if (!invalidAuth(orgResponse)) {\n        return;\n      }\n\n      if (needToRefreshToken(orgResponse)) {\n        await refreshToken();\n        context.retry();\n      } else {\n        context.cancelAll();\n        sendEventToRedirectToLogin();\n      }\n    },\n    (response, context, orgResponse) =\u003e {\n      if (!isOkay(orgResponse)) {\n        context.error(\"not okay\");\n      }\n    }\n  ]\n});\n```\n\nYou can put `headers`. Each value could be a string or a function returning a string. When it's a function, it shouldn't be async.\n\nNext, we see `responseValidations`. You can put an array of functions and they might be async or sync. Each function takes three arguments:\n\n- `response` : A response object which has been transformed by your `transformResponse`.\n- `context` : A context object with functions to be called when it's not successful.\n  - `error(errorCode?: any)` : It returns an error. Once any of `error`, `retry` or `cancelAll` is called, then it will not execute next validation functions. However `after` callbacks will be still executed.\n  - `retry(retryNum = 1)` : If you want to retry, call this function. The result from the retried request will be returned.\n  - `cancelAll()` : It cancels all the other ongoing requests. For example, you can call this when user needs to be logged out due to expired token.\n- `orgResponse` : An original axios response object\n\n```js\nconst { error, errorType, errorCode, response, orgResponse } = await myAuthApi.get(path);\n```\n\n## `ErrorType` Schema\n\n```js\nenum ErrorType {\n  // `retry()` has been called, it retried, but eventually failed\n  // `errorCode` won't contain anything\n  RETRY_DONE_FAILED,\n\n  // `cancelAll()` has been called from this request\n  // `errorCode` won't contain anything\n  CANCELED_ALL,\n\n  // `cancelAll()` has been called from other request, so this request has got canceled\n  // `errorCode` won't contain anything\n  GOT_CANCELED,\n\n  // axios has thrown an exception\n  // `errorCode` will contain exception object\n  EXCEPTION,\n\n  // `error()` has been called\n  // `errorCode` will contain whatever you passed at `error(whatever)`\n  USER_DEFINED_ERROR\n}\n```\n\n## Combining configs\n\nYou can extend api objects like above, however there's another approach. You can combine configs and use it like the following:\n\n```js\nimport { mergeConfigs } from \"apitool\";\n\nconst config1 = {\n  baseURL: ...\n  headers: {},\n  transformData: []\n  transformResponse: []\n  before: []\n  after: []\n  responseValidations: []\n};\nconst config2 = {...};\nconst config3 = {...};\nconst config = mergeConfigs(config1, config2, config3)\n\nconst api = new Api(config);\nconst result = await api.get(path);\n```\n\n## Importing things\n\nAll you can import from `apitool` is the following:\n\n```js\nimport Api, { Response, Context, ErrorType, mergeConfigs } from \"apitool\";\n```\n\n## Contributing\n\n1.  Fork it!\n2.  Create your feature branch: git checkout -b my-new-feature\n3.  Commit your changes: git commit -am 'Add some feature'\n4.  Push to the branch: git push origin my-new-feature\n5.  Submit a pull request :D\n\n## Author\n\nEunjae Lee, Released under the [MIT](https://github.com/eunjae-lee/apitool/blob/master/LICENSE.md) License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feunjae-lee%2Fapitool","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feunjae-lee%2Fapitool","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feunjae-lee%2Fapitool/lists"}