- Saved searches
- Use saved searches to filter your results more quickly
- License
- jalik/js-rest-client
- Name already in use
- Sign In Required
- Launching GitHub Desktop
- Launching GitHub Desktop
- Launching Xcode
- Launching Visual Studio Code
- Latest commit
- Git stats
- Files
- README.md
- Запросы к Rest API из JavaScript компактно и красиво
- Кандидаты
- Небольшое лирическое отступление
- restful.js
- Случайные занимательные факты
Saved searches
Use saved searches to filter your results more quickly
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
Simple and efficient REST Client.
License
jalik/js-rest-client
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
README.md
We often repeat our self when creating a REST client, however there are some little things that could be avoided on each request, like concatenating base URL with path, passing credentials, serializing data.
With this lib, you can build a REST client quickly and almost effortlessly. It can also be used to create an REST SDK, so your users would not have to craft the requests entirely.
Let’s see how to create a REST client.
import RestClient from '@jalik/rest-client'; const client = new RestClient( // The base URL of all requests. baseUrl: 'https://rest.coinapi.io/v1', >);
Fetch is the standard and simplest way to execute HTTP requests in web browsers using Promise . This lib is using fetch() under the hood (via the whatwg-fetch polyfill package), so if you know how to use fetch() , you already know how to craft requests with this REST client.
const body = name: 'Karl' >; client.fetch('/users', method: 'POST', body: JSON.stringify(body), headers: 'content-type': 'application/json' > >) .then((resp) => // handle response >) .catch((error) => // handle error >);
This method is generic, so to make code more concise and readable, you can use any of the next shortcut methods, which correspond to an HTTP verb.
Note about JSON serialization
The fetch() method which is called by all the methods below (delete, get, patch. ) will automatically serialize the request body, if body is an object and the header content-type is application/json .
client.delete('/users/1') .then((resp) => // handle response >) .catch((error) => // handle error >);
client.get('/users', headers: accept: 'application/json' > >) .then((resp) => resp.json()) .then((json) => // handle response >) .catch((error) => // handle error >);
client.head('/users', headers: accept: 'application/json' > >) .then((resp) => // handle response >) .catch((error) => // handle error >);
client.options('/users', headers: accept: 'application/json' > >) .then((resp) => // handle response >) .catch((error) => // handle error >);
const body = name: 'Karl' >; client.patch('/users/1', body, headers: 'content-type': 'application/json' > >) .then((resp) => // handle response >) .catch((error) => // handle error >);
const body = name: 'Karl' >; client.post('/users', body, headers: 'content-type': 'application/json' > >) .then((resp) => // handle response >) .catch((error) => // handle error >);
const body = name: 'Karl' >; client.put('/users', body, headers: 'content-type': 'application/json' > >) .then((resp) => // handle response >) .catch((error) => // handle error >);
Headers can be set when creating the RestClient instance. For example the code below will send the accept header with all requests (while being overridable on each request).
const client = new RestClient( baseUrl: 'https://rest.coinapi.io/v1', headers: accept: 'application/json', > >);
Headers can be get or set at anytime and anywhere in your code.
client.setHeader('accept', '*'); client.getHeader('accept');
Note that all header names are lower cased automatically in getHeader() and setHeader() to make it more bug proof, so the following code is referring to the same header.
// Define 'accept' header equivalents client.setHeader('Accept', '*'); client.setHeader('ACCEPT', '*'); client.setHeader('accept', '*'); // Return 'accept' header equivalents client.getHeader('Accept'); client.getHeader('ACCEPT'); client.getHeader('accept');
If you need to override or remove specific headers for a request it’s still possible by passing these headers in the request options. The next example works with all methods (delete, patch, post. ).
// This will replace the `accept` header only for this request. client.get('/users', headers: accept: 'application/xml' > >); // This will not send current credentials for this request. client.post('/auth', user: 'jalik', pass: 'yun8amginIr#' >, headers: authorization: '' > >);
Finally, if you need to loop through all defined headers.
client.getHeaderNames().forEach((name) => const value = client.getHeader(name); // do something with value. >);
Generally, authenticating against an API is done by providing a header, which could be the classic authorization header or a custom access token header. Below is a list of examples showing some of them.
// Basic authentication client.setHeader('authorization', 'Basic dGVzdDoxMjPCow=='); // Bearer authentication client.setHeader('authorization', 'Bearer mF_9.B5f-4.1JqM'); // Custom access token (api key) client.setHeader('x-api-key', 'Jec5omtyacuzUcCanQuibPyFrajecEif');
Note that there is no mechanism to support token as query parameter because it is a bad practice and not secure (https://tools.ietf.org/html/rfc6750#section-2.3). However, this can still be achieved by passing the access token on each request manually.
History of releases is in the changelog.
The code is released under the MIT License.
Запросы к Rest API из JavaScript компактно и красиво
Делал я тут небольшой проект на чистом JS и в ходе оного потребовалось работать с Rest API. Ну не ручками ведь XMLHttpRequest дёргать, решил я, наверняка есть бесчисленное количество готовых решений для такой простой задачи.
Как можно догадаться по КДПВ, я несколько ошибался; впрочем, обо всём по порядку. Но если вкратце — получился вот такой симпатичный велосипедик, с которым запросы к Rest API получаются, как и обещано в заголовке, компактными и красивыми.
Кандидаты
Итак, мне был нужен джаваскриптовский клиент для Rest API. Гугл выдал чуток библиотек — restful.js, rest, amygdala. На самом деле, была ещё вот такая библиотечка, но это плагин к jQuery. jQuery в проекте не используется и тащить его как-то не хотелось; но, отчасти, предлагаемый библиотекой синтаксис мне понравился и это ещё всплывёт впоследствии.
Amygdala отпала сразу — нет Promise, а значит нет и async/await. Ещё и границы функциональности у неё странные, amygdala скорее претендует на что-то вроде недо-data-layer; про отсутствие сбилженной версии и лишние зависимости я тактично умолчу.
Осталось два кандидата — restful.js и rest.
rest предлагает некое минимальное ядро и даёт широкие возможности по его кастомизации с помощью так называемых «перехватчиков» — interceptors в оригинале. Не знаю насколько это круто — перспектива строить полные урлы и указывать метод руками при каждом запросе меня вовсе не прельщала, перехватчиков для модификации этого поведения не наблюдалось, да и документация восторга не вызывала. Я перешёл к последнему варианту — restful.js.
A pure JS client for interacting with server-side RESTful resources. Think Restangular without Angular.
Вообще-то я предпочитаю Ember, но какая разница? Главное-то что б использовать удобно было!
const articleCollection = api.all('articles'); // http://api.example.com/articles // http://api.example.com/articles/1 api.one('articles', 1).get().then((response) => < const articleEntity = response.body(); // if the server response was < id: 1, title: 'test', body: 'hello' >const article = articleEntity.data(); article.title; // returns `test` article.body; // returns `hello` // You can also edit it article.title = 'test2'; // Finally you can easily update it or delete it articleEntity.save(); // will perform a PUT request articleEntity.delete(); // will perform a DELETE request >, (response) => < // The reponse code is not >= 200 and < 400 throw new Error('Invalid response'); >);
Это пример из документации. Выглядит неплохо в сравнении с конкурентами, да и документация достаточно внятная… Вариантов-то всё равно больше не наблюдается. Берём? Берём.
Небольшое лирическое отступление
Есть такая концепция «разумного умолчания», которая предполагает, если пересказывать своими словами, что решение некоей проблемы заточено, условно, под 90% юзкейсов, а в остальных 10% нужно явно сказать что имеются особенные обстоятельства.
Тривиальный пример этой концепции — параметры по умолчанию в большинстве языков программирования.
Куда менее тривиальный пример (являющийся, тем не менее, просто обобщением предыдущего) — оптимизация любого интерфейса исходя из ожидаемого поведения пользователя, будь то интерфейс командный, графический, программный или вообще физический: рубильники там, кнопочки и прочие хардварные крутилки-вертелки.
Общая стратегия этих оптимизаций одинакова — 90% пользователей для достижения своих целей должны производить так мало действий, как это вообще возможно. Не будет большим преувеличением сказать что это общая формула прогресса — изобретение всё более и более простых, с точки зрения количества необходимых телодвижений, интерфейсов для решения всё тех же задач.
И, в общем-то, эти самые разумные умолчания — один из главных способов упрощения.
Зачем я играю в Капитана Очевидность, растекаясь мысью по древу? Потому что очередной велосипед — это просто очередной велосипед, а вот велосипед с подведённой под него философской базой уже не просто очередной велосипед, а даже может издали и на мотоцикл смахивать!
Конец небольшого лирического отступления.
restful.js
Итак, restful.js. Использовал я его крайне недолго — и пары дней не прошло, как я понял что:
- Каждый раз явно вызывать all() — не круто.
let games = (await api.games.get()).body().data(); //
В общем, классический пример неудобного интерфейса. Я не знаю, влияние ли это ангуляра, который вроде славится своей академичностью, или ориентация скорее на бытиё фреймворком, нежели библиотекой, но предлагаемый restful.js интерфейс мне сильно не понравился. На самом деле, по итогу он понравился мне, наверно, даже меньше чем интерфейс конкурентов — видимо, эффект зловещей долины сказывается: близко к идеалу, но не дотянуло, а от любви до ненависти всего один велосипед.
Что же я сделал?
Выкинул restful.js и накидал два класса, которые за 150 строк кода делали в принципе то же, что и restful.js за 6000 (шесть тысяч, это не опечатка).
Потом подумал, выложил на github, порефакторил, освоил webpack (замечательная штука!), mocha+chai, sinon и travis, выложил на npm и bower, написал документацию, запилил пример и в итоге написал эту статью, чтобы облегчить жизнь тем, кто столкнётся с такой же проблемой.
На данный момент (июнь 2016) там маловато тестов, нет методов HEAD и OPTIONS, сложно получить сырой ответ и слишком мало бейджей в README (всего один, что за позор. ).
Впрочем, это всё легко исправить. Главное что another-rest-client предоставляет понятный и простой интерфейс, с которым мне нравится работать; надеюсь что и не только мне.
Немного кода
var api = new RestClient('https://api.github.com'); api.res(); api.repos('Amareis/another-rest-client').releases('latest').get().then(function(release)< console.log(release); document.write('Latest release of another-rest-client:
'); document.write('Published at: ' + release.published_at + '
'); document.write('Tag: ' + release.tag_name + '
'); >);
Вложенные ресурсы? Запросто:
var api = new RestClient('http://example.com/api/v1'); api.res(< //or it gets object and returns object where resource is available by name dogs: [ 'toys', 'friends'], cats: 0, humans: 'posts' >); /* last string is equal to: api.res('dogs').res(['toys', 'friends']); api.res('cats'); api.res('humans').res('posts'); */ api.dogs(1337).toys.get(); //GET http://example.com/api/v1/dogs/1337/toys api.dogs(1337).friends(2).delete(); //DELETE http://example.com/api/v1/dogs/1337/friends/2 //POST http://example.com/api/v1/humans/me/posts, body="" api.humans('me').posts.post();
С async/await код получается куда веселей:
var me = api.humans('me'); var i = await me.get(); console.log(i); //just object, i.e. var post = await me.posts.post() console.log(post); //object
Случайные занимательные факты
- Почему такое название? Ну, изначально он был просто rest-client. Но это название (а также ещё несколько похожих) занято в npm, да и уникальность так себе, так что я добавил чутка самоиронии и он стал another-rest-client.
- В самом начале своего существования restful.js была очень похожа на первые версии another-rest-client. Потом, видимо, скатилась в энтерпрайзщину.
- В коде another-rest-client всего два комментария (и я возмущён тем, что их слишком много) и оба они содержат проклятья в сторону Javascript, который не позволяет сделать код полностью красивым.
- Я так и не понял чем WTFPL отличается от MIT лицензии.