{"id":28427181,"url":"https://github.com/amareis/another-rest-client","last_synced_at":"2025-12-12T03:23:16.552Z","repository":{"id":8656450,"uuid":"59205474","full_name":"Amareis/another-rest-client","owner":"Amareis","description":"Simple pure TypeScript REST API client that makes your code lesser and more beautiful than without it.","archived":false,"fork":false,"pushed_at":"2022-03-16T13:34:41.000Z","size":227,"stargazers_count":173,"open_issues_count":2,"forks_count":16,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-06-05T12:09:20.283Z","etag":null,"topics":["javascript","rest","rest-client","typescript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/Amareis.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-05-19T12:45:57.000Z","updated_at":"2025-02-20T10:23:04.000Z","dependencies_parsed_at":"2022-08-07T04:16:41.061Z","dependency_job_id":null,"html_url":"https://github.com/Amareis/another-rest-client","commit_stats":null,"previous_names":["amareis/rest-client"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/Amareis/another-rest-client","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Amareis%2Fanother-rest-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Amareis%2Fanother-rest-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Amareis%2Fanother-rest-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Amareis%2Fanother-rest-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Amareis","download_url":"https://codeload.github.com/Amareis/another-rest-client/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Amareis%2Fanother-rest-client/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263420631,"owners_count":23463984,"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":["javascript","rest","rest-client","typescript"],"created_at":"2025-06-05T12:09:31.708Z","updated_at":"2025-12-12T03:23:16.504Z","avatar_url":"https://github.com/Amareis.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"= another-rest-client\n\nSimple REST API client that makes your code lesser and more beautiful than without it.\n\nThere is some rest clients - https://github.com/marmelab/restful.js[restful.js], https://github.com/cujojs/rest[cujojs/rest] or https://github.com/lincolnloop/amygdala[amygdala] - so why you need another rest client? First, because all of this is not maintained anymore :) But also, because with it your code less and more beautiful than without it or with any analogs. Also, its code really simple - less than 300 sloc and (almost) without magic, so you can just read it (and fix, may be?) if something go wrong.\n\nTo prove my words, here is an minimal working code (you can explore more examples https://github.com/Amareis/another-rest-client/tree/master/examples[here]):\n\nAnd it works with typescript!\n\n[source,typescript]\n----\nimport {RestClient} from 'another-rest-client'\n\nconst api = new RestClient('https://api.github.com').withRes({\n    repos: 'releases',\n} as const)\n\napi.repos('Amareis/another-rest-client').releases('latest').get().then((release: any) =\u003e {\n    console.log(release)\n    document.write('Latest release of another-rest-client:\u003cbr\u003e')\n    document.write('Published at: ' + release.published_at + '\u003cbr\u003e')\n    document.write('Tag: ' + release.tag_name + '\u003cbr\u003e')\n})\n----\n\n== Installation\n\nLibrary is available with npm:\n\n[source,shell]\n----\nnpm install another-rest-client\n# or\nyarn add another-rest-client\n----\n\nNow, add it in script tag or require it or import it:\n\n[source,js]\n----\nconst {RestClient} = require('another-rest-client')\nimport {RestClient} from 'another-rest-client'\n----\n\n*ATTENTION:* If you want to use another-rest-client with node.js, you must define XMLHttpRequest before import (https://github.com/driverdan/node-XMLHttpRequest[see here]):\n\n[source,js]\n----\nglobal.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest\n----\n\n== Usage\n\n[source,js]\n----\nconst api = new RestClient('https://example.com')\n----\n\nAnd here we go! First, let's define resources, using `res` method:\n\n[source,js]\n----\napi.res('cookies')         //it gets resource name and returns resource\napi.res(['cows', 'bees'])  //or it gets array of resource names and returns array of resources\napi.res({       //or it gets object and returns object where resource is available by name\n    dogs: [\n        'toys',\n        'friends'],\n    cats: 0,\n    humans:\n        'posts',\n})\n/* last string is equal to:\napi.res('dogs').res(['toys', 'friends'])\napi.res('cats')\napi.res('humans').res('posts') */\n----\n\nNow we can query our resources using methods `get` (optionally gets query args), `post`, `put`, `patch` (gets body content) and `delete`. All these methods returns promise, that resolves with object that given by server or rejects with `XMLHttpRequest` instance:\n\n[source,js]\n----\napi.cookies.get()              //GET https://example.com/cookies\napi.cookies.get({fresh: true}) //GET https://example.com/cookies?fresh=true\napi.cookies.get({'filter[]': 'fresh'}, {'filter[]': 'taste'}) //GET https://example.com/cookies?filter%5B%5D=fresh\u0026filter%5B%5D=taste\n\n//POST https://example.com/cows, body=\"{\"color\":\"white\",\"name\":\"Moo\"}\"\napi.cows.post({color: 'white', name: 'Moo'}).then((cow) =\u003e {\n    console.log(cow)    //just object, i.e. {id: 123, name: 'Moo', color: 'white'}\n}, (xhr) =\u003e {\n    console.log(xhr)   //XMLHtppRequest instance\n})\n----\n\nIf you want query single resource instance, just pass it id into resource:\n\n[source,js]\n----\napi.cookies(42).get()  //GET https://example.com/cookies/42\n\n//GET https://example.com/cookies/42?fields=ingridients,baker\napi.cookies(42).get({fields: ['ingridients', 'baker']})\n\napi.bees(12).put({state: 'dead'})  //PUT https://example.com/bees/12, body=\"{\"state\":\"dead\"}\"\napi.cats(64).patch({age: 3})       //PATCH https://example.com/cats/64, body=\"{\"age\":3}\"\n----\n\nYou can query subresources easily:\n\n[source,js]\n----\napi.dogs(1337).toys.get()          //GET https://example.com/dogs/1337/toys\napi.dogs(1337).friends(2).delete() //DELETE https://example.com/dogs/1337/friends/2\n\n//POST https://example.com/humans/me/posts, body=\"{\"site\":\"habrahabr.ru\",\"nick\":\"Amareis\"}\"\napi.humans('me').posts.post({site: 'habrahabr.ru', nick: 'Amareis'})\n----\n\nYou can use `url` resource method to get resource url:\n\n[source,js]\n----\napi.dogs.url() === '/dogs'\napi.dogs(1337).friends(1).url() === '/dogs/1337/friends/1'\n----\n\nAnd, of course, you always can use ES6 async/await to make your code more readable:\n\n[source,js]\n----\nconst me = api.humans('me')\nconst i = await me.get()\nconsole.log(i)    //just object, i.e. {id: 1, name: 'Amareis', profession: 'programmer'}\nconst post = await me.posts.post({site: 'habrahabr.ru', nick: i.name})\nconsole.log(post)  //object\n----\n== TypeScript\n\nLibrary infer types from schema, passed to `res`. But it returns new resource (or array or object), so to use it\ncorrectly, you need to use `withRes` method, which returns modified original resource:\n\n[source,typescript]\n----\nlet api = new RestClient('https://api.github.com').withRes({\n    repos: 'releases',\n} as const) // as const needed to infer resources names\n\n// correctly infer all this subresources!\napi.repos('Amareis/another-rest-client').releases('latest').get()\n----\n\nYou can then add more resources reusing already typed resource:\n\n[source,typescript]\n----\napi = api.withRes('additional-resource')\n----\n\n**Custom shortcuts currently not working with TypeScript! And shorcuts always will be in typings, even if they are disabled.**\n\n== Events\n\n`RestClient` use https://github.com/allouis/minivents[minivents] and emit some events:\n\n- `request` - when `open` XMLHttpRequest, but before `send`.\n- `response` - when get server response.\n- `success` - when get server response with status 200, 201 or 204.\n- `error` - when get server response with another status.\n\nAll events gets current XMLHttpRequest instance.\n\nOften use case - authorization:\n\n[source,js]\n----\napi.on('request', xhr =\u003e {\n    xhr.setRequestHeader('Authorization', 'Bearer xxxTOKENxxx')\n})\n----\n\nAlso, returns by `get`, `post`, `put`, `patch` and `delete` `Promise` objects also emit these events, but only for current request.\n\n[source,js]\n----\napi.dogs(1337).toys.get().on('success', console.log.bind(console)).then(toys =\u003e \"...\") //in log will be xhr instance\napi.dogs(1337).toys.get().then(toys =\u003e \"...\") //log is clear\n----\n\nYou can use events to set `responseType` XMLHttpRequest property, to handle binary files (and you can compose it with custom decoders, as described below, to automatically convert blob to File object):\n\n[source,js]\n----\napi.files('presentation.pdf').get().on('request', xhr =\u003e xhr.responseType = 'blob').then(blobObj =\u003e \"...\")\n----\n\n== Configuration\n\nAll the examples given above are based on the default settings. If for some reason you are not satisfied, read this section.\n\nAll configuration is done using the object passed to the constructor or method `conf`. Some options are also duplicated by optional methods arguments.\n\n`conf` returns full options. If you call it without parameters (just `conf()`), it gives you current options.\n\n[source,js]\n----\nconsole.log(api.conf())\n/* Defaults:\n{\n    \"trailing\": \"\",\n    \"shortcut\": true,\n    \"shortcutRules\": [],\n    \"contentType\": \"application/json\",\n    \"encodings\": {\n        \"application/x-www-form-urlencoded\": {encode: encodeUrl},\n        \"application/json\": {encode: JSON.stringify, decode: JSON.parse}\n    }\n}*/\n----\n\nIf you want change RestClient host (lol why?..), you can just:\n\n[source,js]\n----\napi.host = 'https://example2.com'\n----\n\n=== Trailing symbol\n\nSome APIs require trailing slash (for example, this is the default behavior in the django-rest-framework). By default another-rest-client doesn't use any trailing symbol, but you can change this:\n\n[source,js]\n----\nconst api = new RestClient('https://example.com', {trailing: '/'})\n//or\napi.conf({trailing: '/'})\n----\n\nOf course, you can pass all you want (`{trailing: \u0026#39/i-have-no-idea-why-you-want-this-but-you-can/\u0026#39}`).\n\n=== Shortcuts\n\nShortcuts - resources and subresources, that accessible as parent resource field:\n\n[source,js]\n----\napi.cars === undefined\nconst cars = api.res('cars')\napi.cars === cars   //api.cars is shortcut for 'cars' resource\n----\n\nBy default, another-rest-client will make shortcuts for defined resources. This behavior can be disabled in three ways:\n\n[source,js]\n----\napi.sounds === undefined\n\n//first way\nconst api = new RestClient('https://example.com', {shortcut: false})\n//or, second way\napi.conf({shortcut: false})\n//or, third way\nconst sounds = api.res('sounds', false)\n\n//and, still...\napi.sounds === undefined\n----\n\nFirst two ways disables shortcuts globally - on all resources and subresources. Third way disables shortcuts locally - in one `res` call. Also, with third way you can locally _enable_ shortcuts (pass `true` as second `res` argument) when globally they are disabled.\n\nLocal disable of shortcuts can solve some name conflicts (when resource shortcut overwrites some method), but, probably, you will not be affected by this.\n\n*It is strongly recommended do not disable the shortcuts, they greatly enhance code readability.*\n\nYou can also add custom shortcuts for resources via rules. Those can be configured via the `shortcutRules` array in the options. When a resource is added all rules will be invoked with the resource name as argument. If the return value is a non-empty string, it will serve as an additional shortcut.\n\nHave a look at this example which will convert strings with dashes into their camel-case counterpart to serve as additional shortcut:\n\n[source,js]\n----\nconst DASH_REG = /(-)(.)/g\nfunction dashReplace(resourceName) {\n    return resourceName.replace(DASH_REG, (match, p1, p2) =\u003e p2.toUpperCase())\n}\n\nconst api = new RestClient('https://example.com', {shortcutRules: [ dashReplace ]})\napi.res('engine-rest')\napi['engine-rest'] // standard shortcut\napi.engineRest     // custom shortcut to improve readability\n----\n\n=== Request content type\n\nWhen you call `post`, `put` or `patch`, you pass an object to be encoded into string and sent to the server. But how it will be encoded and what `Content-Type` header will be set?\nBy default - in json (`application/json`), using `JSON.stringify`. To change this behavior, you can manually set request content type:\n\n[source,js]\n----\nconst api = new RestClient('https://example.com', {contentType: 'application/x-www-form-urlencoded'})\n//or by conf\napi.conf({contentType: 'application/x-www-form-urlencoded'})\n//or by second argument in 'post', 'put' or 'patch'\napi.cookies.post({fresh: true}, 'application/x-www-form-urlencoded')\n----\n\nBy default RestClient can encode data in `application/json` and `application/x-www-form-urlencoded`. You can add (or replace defaults with) your own encoders:\n\n[source,js]\n----\nconst opts = {\n    contentType: 'application/x-my-cool-mime',\n    encodings: {\n        'application/x-my-cool-mime': {\n            encode: (objectPassedToPostPutOrPatch) =\u003e {\n                //...\n                return encodedToStringObject\n            }\n        }\n    }\n}\nconst api = new RestClient('https://example.com', opts)\n//or by conf\napi.conf(opts)\n----\n\nIf there is no suitable encoder, passed object will be passed to the XMLHttpRequest.send without changes.\n\n=== Response content type\n\nWhen server answers, it give `Content-Type` header. another-rest-client smart enough to parse it and decode `XMLHttpRequest.responseText` into object. By default it can decode only `application/json` using `JSON.parse`, but you can add your own decoders:\n\n[source,js]\n----\nconst opts = {\n    encodings: {\n        'application/x-my-cool-mime': {\n            decode: (stringFromXhrResponseText) =\u003e {\n                //...\n                return decodedFromStringObject\n            }\n        }\n    }\n}\nconst api = new RestClient('https://example.com', opts)\n//or by conf\napi.conf(opts)\n----\n\nIf there is no suitable decoder (or server given't `Content-Type` header), gotten `XMLHttpRequest.response` will be passed to Promise.resolve without changes.\n\nOf course, you can combine encoders and decoders for single MIME:\n\n[source,js]\n----\nconst opts = {\n    contentType: 'application/x-my-cool-mime',\n    encodings: {\n        'application/x-my-cool-mime': {\n            encode: (objectPassedToPostPutOrPatch) =\u003e {\n                //...\n                return encodedToStringObject\n            },\n            decode: (stringFromXhrResponseText) =\u003e {\n                //...\n                return decodedFromStringObject\n            }\n        }\n    }\n}\n\nconst api = new RestClient('https://example.com', opts)\n//or by conf\napi.conf(opts)\n----\n\n== Contributing\n\nThat's easy:\n\n[source,bash]\n----\ngit clone https://github.com/Amareis/another-rest-client.git\ncd another-rest-client\nyarn\necho \"//Some changes...\" \u003e\u003e src/rest-client.ts\nyarn build \u0026\u0026 yarn test\n----\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famareis%2Fanother-rest-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famareis%2Fanother-rest-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famareis%2Fanother-rest-client/lists"}