Я разработчик и большую часть моей карьеры я строю API различных сервисов. Рекомендации для этой статьи, были собраны на основе наиболее часто встречающихся проблем при проектировании своего сервиса в команде или использовании сторонних API.
Скорее всего вы сталкивались с провайдерами ужасного API. Работа с ними, как правило, сопряжена повышенной эмоциональностью и недопониманием. Большую часть таких проблем можно избежать проектируя интерфейс приложения используя советы ниже.
1. Не используйте глаголы в URL *
* - если это одна из CRUD-операций.
За действие с ресурсом отвечают CRUD-методы запроса: POST - создать (create), GET - получить (read), PUT/PATH - обновить (update), DELETE - удалить (ну вы поняли). Плохо:
POST /users/{userId}/delete - удаление пользователяPOST /bookings/{bookingId}/update - обновление бронировки
Хорошо:
DELETE /users/{userId}PUT /bookings/{bookingId}
2. Используйте глаголы в URL
Плохо:
POST /users/{userId}/books/{bookId}/create - добавить книгу пользователю
Хорошо:
POST /users/{userId}/books/{bookId}/attachPOST /users/{userId}/notifications/send - отправить уведомление пользователю
3. Выделяйте новые сущности
Выше есть пример добавления книги пользователю, возможно, логика вашего приложения подразумевает список избранного, тогда роут может быть и таким:
POST /wishlist/{userId}/{bookId}
4. Используйте один идентификатор ресурса *
* - если ваша структура данных это позволяет.
Это значит если у вас есть записи вида один ко многим,
например
бронь -> путешественники (booking->travellers), вам будет
достаточно передавать в запросе идентификатор путешественника.
Плохо:
# получение данных путешественникаGET /bookings/{bookingId}/travellers/{travellerId}
Хорошо:
GET /bookings/travellers/{travellerId}
Так же замечу что /bookings/travellers/
лучше чем
просто /travellers
хорошо придерживаться иерархии
данных в своем API.
5. Все ресурсы во множественном числе
Плохо:
GET /user/{userId} - получение данных пользователяPOST /ticket/{ticketId}/book - бронирование билета
Хорошо:
GET /users/{userId}POST /tickets/{ticketId}/book
6. Используйте HTTP-статусы по максимуму
Самый простой способ обработки ошибок - это ответить соответствующим кодом состояния. В большинстве случает один этот статус может дать исчерпывающую информацию о результате обработки запроса. Одни из самых распространенных кодов ответов:
-
400 Bad Request - клиент отправил неверный запрос, например, отсутствует обязательный параметр запроса.
-
401 Unauthorized - клиенту не удалось пройти обязательную аутентификацию на сервере для обработки запроса.
-
403 Forbidden - клиент аутентифицирован, но не имеет разрешения на доступ к запрошенному ресурсу.
-
404 Not Found - запрошенный ресурс не существует.
-
409 Conflict - этот ответ отправляется, когда запрос конфликтует с текущим состоянием сервера.
-
500 Internal Server Error - на сервере произошла общая ошибка.
-
503 Service Unavailable - запрошенная услуга недоступна.
7. Модификаторы получения ресурса
Логика построения роутов может быть не связана с архитектурой проекта или структурой базы данных. Например, в бд есть викторины и пройденные викторины - две отдельные таблицы (quizzes и passed_quizzes). Но для апи это могут быть просто викторины, а пройденные викторины это модификатор.
Пример: /quizzes
и /quizzes/passed
.
Здесь quizzes
- ресурс (викторины),
passed
- модификатор (пройденные).
Плохо:
GET /passed-quizzes - получение пройденных викторинGET /booked-tickets - получение забронированных билетовPOST /gold-users - создание премиум пользователя
Хорошо:
GET /tickets/bookedPOST /users/gold
8. Выберите одну структуру ответов
Когда на два запроса к API может быть получен совсем разный по структуре ответ - это грустно. Старайтесь сформировать одну четкую структуру которой всегда будете придерживаться, будет круто еще включить служебные поля, несущие дополнительную информацию.
Плохо:
GET /book/{bookId}{ "name": "Harry Potter and the Philosopher's Stone", "genre": "fantasy", "status": 0, # статус вашего приложения "error": false, ...}
Хорошо:
GET /book/{bookId}{ "status": 0, "message": "ok", "data": {...}}
В этом примере 3 поля универсальны и могут использоваться для
любого ответа от апи. status
, message
-
собственный статус и сообщение приложения по которому клиент сможет
ориентироваться, эти поля сообщат ему дополнительную информацию о
процессе обработки запроса, но не данные ресурса. Например, в нашем
приложении в один момент времени, пользователь может проходить
только одну викторину. Тогда запрос на начало новой может выдать
409й статус а в полях status
и message
дополнительную информацию почему была получена ошибка.
9. Все параметры и json в camelCase
9.1 В параметрах запросов
Плохо:
GET /users/{user-id}GET /users/{user_id}GET /users/{userid}
Хорошо:
GET /users/{userId}POST /ticket/{ticketId}/gold
9.2 В теле ответа или принимаемого запроса
Плохо:
{ "ID": "fb6ad842-bd8d-47dd-b7e1-68891d8abeec", "Name": "soccer3000", "provider_id": 1455, "Created_At": "25.05.2020"}
Хорошо:
{ "id": "fb6ad842-bd8d-47dd-b7e1-68891d8abeec", "Name": "soccer3000", "providerId": 1455, "createdAt": "25.05.2020"}
10. Пользуйтесь Content-Type
Плохо:
GET /tickets.jsonGET /tickets.xml
Хорошо:
GET /tickets// и в хедереСontent-Type: application/json// илиСontent-Type: application/xml
Заключение
Перечисленные выше рекомендации это далеко не весь список способов сделать API лучше. Для дальнейшего изучения рекомендую разобрать спецификации REST API и список кодов http-статусов (вы удивитесь на сколько их много и какие ситуации они охватывают).
А в комментариях, предлагаю написать свою рекомендацию по построению REST API которую вы считаете важной.