# 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