---
# System prepended metadata

title: HTTP based API styleguide proposal

---

# 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
