# REST обертка для axios/axios

Простая обертка для популярного HTTP клиента на javascript. Необходима для упрощения запросов к REST api из фронтенд клиента. Добавляет также функцию задержки выполнения запроса для тестирования отзывчивости и интуитивности интерфейса пользователя. Есть два варианта применения библиотеки. Когда у вас единственный сервер API по умолчанию или когда у вас несколько разных серверов/сервисов API. В начале речь идет об использовании единственного сервера.

Зависит от `axios` и `underscore` - внешние зависимости. Их нужно подключить отдельно. Либо установить в проекте либо подключить отдельно в HTML для браузера.

## Как работает

Просто создайте переменную для работы с API и используйте её где вам это необходимо. Экземпляр класса может быть глобальным или расширять возможности другой библиотеки, фреймворка. Создавался для использования в приложениях VUE расширяемых через плагин или `Vue.prototype.$api = api;`.

```javascript
import Api from 'axios-rest-api';

var api = new Api();
// null - параметры запроса
// данный вариант вызова возможен, но наименее привлекателен
api.load(null, 'https://www.google.com?q=123').then((response) => {
  // ... вызов гугла только как пример
})
.catch((e) => {
  // ... скорее всего вы получите 405 ошибку
});

// Здесь параметры запроса уже вынесены из URL в параметры вызова метода
api.load({g: 123}, 'https://www.google.com');

// Здесь и параметры убраны, но и сам URL отсутствует
api.res('google').load({g: 123}); // URL вынесен в конфигурацию, описание ниже
```

## Методы запросов

Помимо стандартных *get* и *post* имеются не каноничные *cget, load, save, new, edit, remove*. Кроме прочего, *get* подразумевает наличие ID ресурса в качестве обязательного параметра запроса по умолчанию, ведь это библиотека для запросов к REST API. Когда вам понадобится просто запросить URL без праметров, используйте метод `load(null, url)` как в примере выше или сконфигурируйте свой роутер запросов.

Когда вам нужно делать простые запросы GET/POST к серверу, вам не нужна обертка над axios, тогда просто используйте axios.

## Настройка

Подразумевает установку значений по умолчанию для большинства запросов, таких как *базовый URL* и *шаблоны запросов*(роутер). У вас будет либо единственный *ресурс по умолчанию*, либо список ресурсов, которые затем необходимо передать в конструктор класса. Конфигурирование URL запросов полностью вынесено в настройки именно для удобства и простоты использования. Хотя писать полные или относительные URL запросов в методах вам никто не запрещает.

```javascript
import Api from 'axios-rest-api';

const resources = [];

resources.default = {
  host: 'http://127.0.0.1',
  prefix: 'api',
};

resources.users = {
  routing: {
    cget: 'popular',
  },
};

var api = new Api(resources);
api.res('users').load(); // GET http://127.0.0.1/api/users
api.res('users').cget(); // GET http://127.0.0.1/api/users/popular
api.res('users').get({id: 1}); // GET http://127.0.0.1/api/users/1  
```

### Ресурсы

Основа запросов заключается в получении *ресурсов* с единственного или нескольких серверов. Метод `res(name|url)` устанавливает *имя* ресурса или *адрес* его нахождения. Далее имя ресурса сопоставляется со списком сконфигурированных ресурсов для настройки конкретного *запроса*, если это необходимо. При остсуствии специфических настроек ресурса берется конфигурация *по умолчанию*, а при её отсутствии, запрос сформируется по общепринятым правилам. Метод возвращает сконфигурированный экземпляр класса для дальнейшей работы с ресурсом.

Имя ресурса используется для подстановки в итоговый URL запроса как есть. Поэтому вы можете не заморачиваться с настройкой роутов, а просто вызывать ресурс с сервера по относительному адресу

```javascript
resources.default = {
  host: 'http://127.0.0.1',
  prefix: 'api',
};

var api = new Api(resources);
api.res('users/new').post({name: 'Bob'});
// POST http://127.0.0.1/api/users/new
// send data: {name: 'Bob'}
```

### Параметры запросов

Библиотека меняет порядок параметров в методе запросов(например *get* или *post*), и в отличии от Axios значению *url* отводится наименьший приоритет. URL либо не указывается вообще либо указывается самым последним параметром. На первое место выводятся либо *данные* для POST запроса, либо *параметры* URL для GET запросов.

```javascript
resoures.google = {
  host: 'https://www.google.com',
  routing: {
    get: 'search',
  },
};

var api = new Api(resoures);
api.res('google').get({q: 123, hl: 'ru'});
// GET https://www.google.com/search?q=123&hl=ru
```

## Роутинг

Именно для простоты вызова ресурсов в библиотеке испрользуется *роутинг*. Подразумевается, что все REST запросы к API стандартизованы. При обычной архитектуре API сервера вам даже не придется настраивать роутинг для ваших ресурсов. Настройка роутинга, это простой JS объект с указанием *названия* метода запроса и *шаблона запроса*, который перерисывает значения предустановленные этой библиотекой.

Подстановочные значения URL указываются в фигурных скобках и берутся из списка параметров для запроса. Если подстановок в шаблоне нет, параметры уйдут в *query* часть URL. Часть параметров может уйти в подстановку, а оставшиеся в параметры URL, если такая ситуация будет необходима.

```javascript

resources.users = {
  host: 'http://127.0.0.1',
  routing: {
    get: '{id}', // по умолчанию такой же
    cget: 'list',
    load: 'any/other/user/{id}'
  },
};
api.res('users').get({id: 1}); // GET http://127.0.0.1/api/users/1
api.res('users').get({id: 1, sort: 'asc'}); // GET http://127.0.0.1/api/users/1?sort=asc
api.res('users').cget(); // GET http://127.0.0.1/api/users/list
api.res('users').post({name: 'Bob'}); // POST http://127.0.0.1/api/users
api.res('users').load({id: 1, sort: 'asc'});
// GET http://127.0.0.1/api/users/any/other/user?id=1&sort=asc
```

Стандартный роутер библиотеки. В комментариях реальный метод запроса отправляемого на сервер. Из них *get, post, put, patch, delete, options* являются общепринятыми, остальные добавлены библиотекой и не являются обязательными.

```javascript
{
  route: resources, // имя ресурса
  load: '', // GET
  get: '{id}', // GET
  cget: '', // GET
  send: '', // GET !!!
  post: '', // POST
  save: '', // POST
  remove: '', // POST
  new: '', // POST
  edit: '', // POST
  delete: '{id}', // DELETE
  put: '{id}', // PUT
  patch: '{id}', // PATCH
  option: '{id}', // OPTIONS
}
```

## Задержка запросов

Библиотека позволяет установить задержку в секундах на выполнение любого запроса. Ее можно сконфигурировать как настройку по умолчанию, для каждого ресурса в целом, для конкретного запроса индивидуально. Все эти значения могут различаться, но, если вы действительно захотите использовать что-то большее чем просто общую задержку для всех запросов по умолчанию, есть особенность. Приоритет значения задержки указан именно в такой последовательности.

Например, когда общее значение по умолчанию(5 секунд) выше любого другого значения(2 секунды), будет использовано именно оно - значение по умолчанию 5 секунд.

```javascript
resources.default = {
  delay: 5,
  host: 'http://127.0.0.1',
  prefix: 'api',
};

resources.users = {
  delay: 3,
};

resouresourcesres.comments = {
  delay: 2,
};

var api = new Api(resources);
api.res('users').load(); // реальная задержка 5 секунд
api.res('comments').load(); // реальная задержка 5 секунд
api.res('users').load(null, null, 4); // реальная задержка 5 секунд
api.res('users').setDelay(3).load(); // реальная задержка 5 секунд
api.res('autors').load(); // реальная задержка 5 секунд
// Все значения перебиты максимальным (default)
```

Обратная ситуация, когда при уменьшении приоритета будет увеличиваться время задержки запроса, будут использоваться именно эти значения.

```javascript
resources.default = {
  delay: 1,
  host: 'http://127.0.0.1',
  prefix: 'api',
};

resources.users = {
  delay: 2,
};

resources.comments = {
  delay: 3,
};

var api = new Api(resoures);
api.res('users').load(); // реальная задержка 2 секунды
api.res('comments').load(); // реальная задержка 3 секунды
api.res('users').load(null, null, 4); // реальная задержка 4 секунды
api.res('users').setDelay(3).load(); // реальная задержка 3 секунды
api.res('autors').load(); // реальная задержка 1 секунда (default)
```

## Несколько серверов API

По сказанному выше видно, что описание ресурса включает в себя в том числе перечисление настроек подключения к серверу АПИ. Когда у вас единственный сервер, эти настройки обычно легче указать в описании стандартного подключения по умолчанию. В самих же ресурсах перечислять настройки касающиеся непосредственно ресурсов. Такая путаница, или "магия", необходимы для простоты использования библиотеки.

Имя конфигурации подключения к серверу АПИ указывается вторым параметром при вызове ресурса.

```javascript
api.res('comments', 'post').get({id: 123});
// Загрузить комментарии к посту номер 123 с сервера АПИ постов,
// описанном в ресурсе под именем 'post'
```

Далее, вы точно также конфигурируете ресурсы, за исключением только того, что какой-то выбранный вами лично ресурс, будет содержать настройки подключения к новому АПИ. Хорошо, если имя этого ресурса(описание подключения к новому АПИ) будет уникально. Но нет ничего плохого в том, чтобы описать получение единственного ресура с единственного сервера АПИ в одной инструкции.

```javascript
resources.default = {
  host: 'http://localhost',
  prefix: 'api',
};

resources.users = {
  host: 'http://api-server-first',
};

resources.auth = {
  host: 'http://second-server-api',
};
```

В примере выше, все ресурсы распределены между тремя серверами. Два из них четко `users` и `auth` описаны отдельно, каждый на своем сервере АПИ. Любые остальные запрашиваемые у библиотеки ресурсы будут направляться на сервер по умолчанию.

```javascript
// Ресурс или АПИ с именем 'post' не существует в новой конфигурации.
// Будет вызван ресурс 'comments' с сервера по умолчанию
api.res('comments', 'post').get({id: 123}); // http://localhost/api/comments/123

api.res('profiles', 'users').load(); // GET http://api-server-first/api/profiles
api.res('credentials', 'auth').post({login: 'Bob'}); // POST http://second-server-api/api/credentials

// Получение единственного ресурса АПИ по имени конфигурации АПИ
// Имя ресурса и имя АПИ совпадают
api.res('users').get({id: 3}); // GET http://api-server-first/api/users/3
```

### Один ресурс на разных серверах

Объединение описания и ресурсов и самого подключения к серверу АПИ может запутать, но это необходимо именно для простоты использования. Иначе вам приходится в каждом вызове явно указывать и имя подключения и имя ресурса в других существующих библиотеках. В этой библиотеке имя подключения или имя самого ресурса могут отсутствовать в вызове ресурса или не существовать вовсе.

```javascript
resources.default = {
  host: 'http://localhost',
  prefix: 'api',
};
resources.first = {
  host: 'http://api-server-first',
  version: 'v1',
};
resources.second = {
  host: 'http://second-server-api',
  version: 'v2',
};

resources.users = {
  routing: {
    cget: 'popular',
  },
};

api.res('users').cget(); // GET http://localhost/api/users/popular
api.res('users', 'first').cget(); // GET http://api-server-first/api/v1/users/popular
api.res('users', 'second').cget(); // GET http://second-server-api/api/v2/users/popular

// Получить не описанный в настройках ресурс 'posts' с описанного подключения 'second'
api.res('posts', 'second').get({id: 5}); // GET http://second-server-api/api/v2/posts/5
```

## Значения по умолчанию

Для всех запросов можно установить значения по умолчанию через метод `default(key, val)` или получить установленное значение `default(key)`. Эти значения используются внутри библиотеки для формирования запроса. Например, установка ключа авторизации. Вы можете использовать их также, при необходимости.

## Авторизация

Дополнительно библиотекой реализована установка ключа авторизации в заголовок запроса `Authorization: Bearer`. Его нужно устанавливать для каждого запроса через `setAuthKey(authkey)`, или прописать параметр `key` в конфигурации ресурса, или установить значение по умолчанию для библиотеки `default('key', key)`. При формировании запроса ресурса ключ авторизации берется из конфигурации ресурса, а если его там нет, из значений по умолчанию. Значение установленное через `setAuthKey(authkey)`, оно перезаписывает для этого запроса все предыдущие.

## Костыли

Вы можете перезаписать установленный в конфигурации ключ авторизации указав вторым параметром имя ресурса `setAuthKey(authkey, name)`

Вы можете использовать `saveAuthKey(authkey, name)` вместо `setAuthKey(authkey, name)` когда нужно сохранить ключ в конфиге, но не устанавливать его для следующего запроса.

Вы можете установить свои заголовки запроса через `setHeaders(headers)`

Вы можете определить ресурс после создания экземпляра библиотеки через `define(name, resource)` указав имя и описав объект его конфигурации вторым параметром.

Вы можете расширить конфигурацию ресурса или перезаписать некоторые поля через `extend(name, resource)`, вложенные объекты копируются по ссылке.

Вы можете установить объект _конфигурации_ Axios через `setAxiosConfig(config)` или получить его `dumpAxiosConfig()`. Но он очищается после каждого запроса.

Вы можете сами выполнить свой запрос по схеме `request(method, action, data, params, url, wait)` где _action_ - это имя запроса, ключ для роутера, например `post`, `load` или `save`.

Вы можете получить Axios целиком методом `axios()`

Вы можете получить сгенерированный URL через `lastURL()`
