# HTTP based API styleguide proposal
## Основные концепции
- Использовать иерархии объектов при создании методов, как в REST
- Использовать существительные в эндпоинтах для обозначения объектов\ресурсов
- Использовать глаголы в эндпоинтах для обозначения действий над объектами
- Разделять операции на чтение и все прочие
- Использовать ограниченный набор методов HTTP для работы с API
- Информировать о результатах выполнения запроса на нескольких уровнях - HTTP кода ответа и данных из ответа
## URL
URL должен состоять из двух частей:
- **Ссылки** на ресурс (обязательно).
- **Метода**, связанного с ресурсом (опционально).
**Ссылка** на ресурс может включать:
- наименования сущностей - организованные иерархически **существительные**, отображающие суть ресурса (обязательно).
- идентифицирующие параметры - идентификаторы конкретных сущностей (опционально).
**Метод** должен быть связан с конкретным ресурсом, идентифицируемым через первую часть URL (ссылку).
Метод всегда завершает url и всегда должен быть **глаголом**.
Методы могут отсутсвовать, в таком случае для данного эндпоинта разрешены только операции чтения через метод GET (см. [Методы HTTP](#методы-http)).
Методы используются для описания любых операций, не являющихся операциями чтения и требуют обращения через метод POST (см. [Методы HTTP](#методы-http)).
Общая схема URL выглядит следующим образом:
```
/{relationship-name}[/{resource-id}]/.../{relationship-name}[/{resource-id}][/{method}]
```
### Прочие рекомендации
- Использовать косую черту "/" для обозначения иерархических отношений
- Не использовать завершающую косую черту
- При наличии нескольких слов в части URL, разделять их при помощи дефисов "-"
- Использовать только lowercase ascii символы
### Примеры
```
POST /devices/create
POST /firmwares/upload
POST /users/<user-id>/workspaces/create
POST /routing-table/<record-is>/drop
POST /customers/<customer-id>/billing-reports/<report-id>/recalculate
GET /customers/<customer-id>/billing-reports/<report-id>
GET /proprietary-messages/history
GET /users/<user-id>/workspaces
```
Примеры маппинга функций на URL
```
select_devices(Optional[DevEUIs]) -> GET /devices?DevEUIs=...
drop_devices(DevEUIs) -> POST /devices/drop {"DevEUIs": ...}
update_workspace_token(workspace_id, token) -> POST workspaces/<workspace_id>/update-token {"token": ...}
update_firmware(gateway_id, firmware) -> POST gateways/<gateway_id>/update-firmware {"firmware": "b64encoded..."}
get_user_info(user_id) -> GET users/<user_id>
```
### Почитать
- https://restfulapi.net/resource-naming/
## Методы HTTP
Используются только следующие HTTP-методы:
- GET
- POST
### GET
GET методы допустимы только для URL, не всегда предполагают операцию чтения.
Или, в более общем смысле, операцию, не изменяющие состояние сервера.
Такие методы допустимы для эндпоинтов, не содержащих метода в URL (см [Url](#Url)).
Примеры операций GET:
- получения информации об объекте
- получение ифнормации о состояния задачи
- получение списка объектов
При передаче GET дополнительные параметры должны передаваться в query.
```
GET /multicast-groups/<group-id>
GET /usage?from=<dt1>&to=<dt2>
GET /user/<user-id>/devices
GET /user/<user-id>/devices/<device-id>
```
### POST
Все операции не являющиеся операциями чтения должны отправляться методом POST.
При передачи POST дополнительные параметры должны передаваться в body. Примеры операций POST:
- создание новой сущности
- удаление сущности
- изменение сущности
- постановка задачи на выполнение
```
POST /multicast-groups/create {"name": "mygroup"}
POST /devices/<device-id>/drop
POST /user/<user-id>/update-dev-eui {"devEui": 123}
POST /tasks/submit
```
### Почитать
- https://williamdurand.fr/2014/02/14/please-dont-patch-like-that/
## Статусы ответов и обработка ошибок
API должен использовать различные HTTP коды ответа в зависимости от результатов обработки запроса.
Рекомендуется использовать следующие коды ответа HTTP:
- 200 - успешно обработанный запрос
- 400 - ошибка со стороны пользователя
- 500 - внутренняя ошибка сервера
Так же допустимо использовать прочие коды ответа, с соблюдением логики:
- 200, 201, 202, 204 - успешно обработанный запрос
- 400, 401, 403, 404, 405, 409, 422, 429 - ошибка пользователя
- 500, 501, 502, 503, 505 - ошибка сервера
На все запросы, обработанные сервисом, должно отправляться тело ответа:
- В случае успешной обработки - передаваться данные.
- В случае ошибки - передаваться информацию об ошибке.
Для любого ответа от API должны выполняться следующие требования:
- Успех от ошибки можно отличить по статусу
- Успех от ошибки можно отличить по содержимому тела ответа
При этом, клиенты, использующие API, должны в первую очередь полагаться на статус ответа.
Это необходимо для случаев, когда проблема возникает не на стороне сервиса, а в сегменте сети.
Тело ответа в случае ошибки должно содержать в себе обязательные поля:
- Код ошибки. Строковый код ошибки, позволяющий явно идентифицировать возникшую проблему на уровне приложения.
- Текст ошибки. Поле должно содержать человеко-читаемое описание возникшей проблемы
Может выглядеть следующим образом: **_(Обсудить)_**
```json
{
"detail": {
"errorCode": "string error code",
"errorDescription": "Human-readable error description"
}
}
```
### Почитать
- https://core.telegram.org/api/errors
- https://dev.vk.com/reference/errors
- https://api.mattermost.com/#tag/errors
- https://docs.github.com/en/rest/guides/best-practices-for-integrators#dealing-with-api-errors
- https://docs.digitalocean.com/reference/api/api-reference/#section/Introduction/HTTP-Statuses
## Версионирование
Приведены примеры версионирования в зависимости от масштаба:
- Эндпоинт (endpoint) - изменения изолированного эндпоинта, не связанного с другими.
- `POST /devices/create.v1`
- `POST /devices/create/v1`
- Ветвь (branch) - изменение нескольких эндпоинтов, организованных в группу.
- `POST /devices/v1/create`
- Сервис (service) - изменение на уровне приложения, требующее изменения версии на большинстве или на всех конечных точках.
- `POST /v1/devices/create`
Предлагается использование **версионирование на уровне сервиса через URL** в виде: ```http://service.com/api/v1/products```.
Прочие варианты версионирования не используются.
При организации нескольких сервисов под единой точкой входа (api gateway), версионирование по-прежнему должно осуществляться на уровне сервисов,
а api gateway должен выполнять трансляцию url и передачу запроса на нужный сервис по следующему правилу:
```
api gw: /{resource}/{version}/{url} -> service: /{version}/{resource}/{url}
```
Например:
- *api gateway:* `/devices/v2/<device-id>/delete` -> *devices api:* `/v2/devices/<device-id>/delete`
- `http://api-gateway.com/api/workspaces/v1/create` -> `http://workspaces-api.com/api/v1/workspaces/create`
### Почитать
- https://www.freecodecamp.org/news/how-to-version-a-rest-api/
## Pagination
Пагинация должна быть реализована для эндпоинтов, возвращающих списки объектов. Пагинация должна быть реализована через offset/limit, передаваемых через query.
```
/devices?offset=0&limit=25
/workspaces?offset=0,limit=25
```
## Примеры API
### Task list API
```
POST /tasks/create {"title": "...", "text":"...", "tags": [...]}
GET /tasks/
GET /tasks/<task-id>
POST /tasks/<task-id>/delete
POST /tasks/<task-id>/update
POST /tasks/<task-id>/toggle-resolved
GET /tasks/<task-id>/tags
GET /tags/
GET /tags/<tag-name>/tasks
```
### Workspace API
```
POST /workspaces/create
GET /workspaces
GET /workspaces/<workspace-id>
POST /workspaces/<workspace-id>/update-auth-token
POST /workspaces/<workspace-id>/update-limits
POST /workspaces/<workspace-id>/reset-counters
POST /workspaces/<workspace-id>/delete
```
### Own book library API
```
POST /books/create {"title":..., "genres": [<genres-id>, "authors": [<authors-id>]]}
GET /books -> all books list
GET /books/<book-id> -> specific book
POST /books/<book-id>/update -> update book
POST /books/<book-id>/delete -> delete book
POST /genres/create -> create genre
GET /genres -> genre list
GET /genres/<genre-id> -> genre with description
POST /genres/<genre-id>/update -> genre update
POST /genres/<genre-id>/delete -> genre delete
GET /genres/<genre-id>/books -> books by genre
POST /authors/create
GET /authors
GET /authors/<author-id>
POST /authors/<author-id>/update -> author update
POST /authors/<author-id>/delete -> author description
GET /authors/<author-id>/books -> books by author
```
---
## Материалы
### Примеры API
- https://dev.vk.com/reference
- https://core.telegram.org/api/
- https://api.slack.com/apis
- https://www.mediawiki.org/wiki/API:Main_page
- https://api.mattermost.com/
- https://docs.github.com/en/rest
- https://docs.gitlab.com/ee/api/
- https://developers.google.com/blogger/docs/3.0/using
- https://docs.microsoft.com/en-us/rest/api/azure/
- https://www.dropbox.com/developers/documentation/http/overview
- https://docs.digitalocean.com/reference/api/api-reference/
- https://developer.twitter.com/en/docs
### Прочие материалы
- https://cloud.google.com/files/apigee/apigee-web-api-design-the-missing-link-ebook.pdf
- https://www.youtube.com/watch?v=HEoTKXpZniY