Карьера в IT. Системный аналитик, часть 8.2. Best Practices, которые стоит использовать при проектировании REST API
Всем привет.
Небольшое предисловие. Я осознаю, что этим постом я вступаю на охрененно тонкий лёд. Если уж к моему предыдущему посту были претензии за то, что я посмел использовать HTTP 404, то уже интересно, какие комментарии последуют после выхода этого поста, в любом случае - you are welcome!
Но тут стоит уточнить, что все те подходы (разные), по которым мы проектируем сервисы - они разные как раз потому, что нет единых mandatory правил к архитектуре приложений, которым если не следуешь - твоя система ломается и больше никогда в жизни не заработает, даже если ты исправишь ее. Есть лишь РЕКОМЕНДАЦИИ, а их многие интерпретируют по-разному и это тоже нормально. Для кого-то свойственно не использовать коды ошибок вообще и передавать их в теле ответа с HTTP 200, для кого-то нет. Ни один из этих подходов не является не правильным.
И нет никаких технических ограничений в принципе. Ты можешь спокойно использовать метод GET для удаления объекта, если ты его так напишешь (не делайте так) или использовать метод PUT, вместо POST, для создания объекта (так уже можно, если понимаешь почему). Главное, чтобы ты понимал как эти тонкости реализации правильно применять. Если сомневаешься - используй методы по классике, хуже от этого он работать не будет.
Да, можно уже прям сейчас кидать тапками.
Теперь уже к основному телу сабжа. Сейчас расскажу про ряд лучших практик, которые можно применять. @VRock, ты как раз спрашивал по поводу конвенции о наименовании ресурсов, тут про это тоже будет.
1. Имя endpoint'а - это существительное, а не глагол. Это одна из самых распространенных ошибок, которые я когда либо встречал (и сам совершал, естественно). Например, было в моей практике и такое - POST /generateMultipleDocument.
Тут важно понимать, что метод - это уже глагол и еще раз дублировать его в наименовании эндпоинта не нужно.
В идеале, в данном варианте будет POST /documents
Не везде от этого можно избавиться, но в большинстве случаев всё-таки можно, если потратить время на придумыванием вариантов (опять же - по факту нейминг ни на что, кроме красоты и структурированности вашего проекта не влияет. А на сколько это важно - решать вам или вашей команде).
1.1. Используйте множественное число. В большинстве случаев, при проектировании методов, работающих с вашим ресурсом - эти методы будут работать не с единственный экземпляром этого ресурса, поэтому название эндпоинта должно быть во множественном числе.
Если же нужно указать, что из всего массива экземпляров ресурса вам нужно получить\обновить\удалить какой-то конкретный, то помещайте идентификатор этого ресурса в URL, передавая его в path.
Например, вот так:
/documents
/documents/
Вместо:
/document
/document/
1.2. Используйте "/" для обозначения иерархии и в принципе используйте вложенность ресурсов.
Например, если мы именуем наш ресурс, как users//playlists//songs - это значит у мы хотим работать со всеми песнями, конкретного плейлиста конкретного пользователя. И сразу понятна иерархичность этих ресурсов.
1.3. Не используйте "/" как закрывающий символ вашего URI.
Вариант users//playlists//songs сильно лучше, чем users//playlists//songs/
1.4. Используйте "-" для разделения составных слов.
Заглавные буквы использовать нельзя, поэтому привычный lowCamelCase нам не подойдет. Если писать всё слитно - очень не читабельно.
Поэтому вместо /applications//creditcardhistory, куда лучше использовать /applications//credit-card-history.
2. Не забывайте про версионирование микросервиса. Почти любой сервис с течением времени развивается и обрастает все большим количеством функций. Если сервис при создании получил версию 1.0.0, то при добавлении какой-нибудь логики в него, добавлении нового метода или полного рефакторинга - версия должна измениться.
Например:
host/v2/documents вместо host/v1/documents после внесения мажорной доработки.
Основные правила версионирования - в случае, если меняется логика незначительно, не добавляется/изменяется обязательность атрибутов, то инкрементируется минорная версия.
В случае если был полный или частичный рефакторинг, менялись обязательные параметры (например, добавлен новый атрибут, который является обязательным), возможно при добавлении нового метода (тут вопрос к разработчикам, в этом случае тоже мажорная версия повышается или т.к. это не влияет на работу подписантов то пофиг?) - инкрементируется мажорная версия.
В этом случае, все подписанты (системы, использующие ваш сервис) вашего микросервиса должны в обязательном порядке переехать на новую версию вашего микросервиса, иначе они не смогут взаимодействовать с ним. Например, если вы добавили обязательный атрибут, то они будут получать в ответ на каждый запрос ошибку, если не доработаются и не начнут его передавать, что приведет к полной поломке этого процесса.
Однако, это не всегда обязательно - в случае, если появляется такая мажорная доработка, но ваши подписанты не готовы дорабатываться одновременно с вами (причин этому может быть множество) - вы можете выкатить одновременно две версии микросервиса, v1 и v2 и поддерживать их обе. Те, кто доработался будут использовать v2, остальные предыдущую версию. Это несет неудобства и затраты, но в любом случае лучше, чем допускать неработоспособность интеграции. В дальнейшем, когда все ваши подписанты доработаются - поддержку предыдущей версии можно остановить.
Примечание: структура версионирования такова: первая цифра - это мажор, вторая цифра - это минор, третья цифра - это патч. Про первые две я уже сказал, а последняя используется только разработчиками. Насколько я понимаю, она повышается вообще каждый раз когда вносят изменения в сервис, но тут могу быть не прав.
3. Используйте пагинацию.
Отправка большого объема данных на фронт, в ответ на его запрос о получении информации по массиву каких-либо объектов, не самая лучшая идея. Как минимум, если вернуть ему тысячи объектов, лежащих в базе и попадающих под выборку - он столько все равно не отобразит, но очень задумается.
Поэтому принято выполнять пагинацию таких данных (от слова page - страница), т.е. возвращать ему часть всей коллекции в каждом запросе. Например - 15, 30, 50 элементов + информацию о текущем положении полученной информации в общей выборке. Почитать про это можно более подробно где-нибудь тут (первая попавшаяся ссылка, я не вчитывался, не реклама).
4. Используйте коды ответов HTTP правильно и эффективно.
Их достаточно много (https://developer.mozilla.org/ru/docs/Web/HTTP/Status) и их можно использовать по назначению. Все знать и использовать не обязательно, но вот примеры их использования
Использовать 201 "Created", вместо 200 "OK", в случае если вы в POST действительно создаете какой-то ресурс. Используется только в POST (ну и в PUT, в ряде частных случаев).
Использовать 204 "No Content", вместо 200 "OK" для DELETE. Это ответ на успешный запрос и он не будет возвращать тело, что и не нужно для данного метода.
Не забывайте использовать 401 "Unauthorized", 403 "Forbidden" и 404 "Not found" вместо безликого 400 "Bad Request", когда это уместно. Как правило 400 кодом пользуются когда нужно ответить на какую-то ошибку валидации или в случае возникновения бизнесовой ошибки, которую вы заранее можете предсказать (очень настоятельно рекомендую хотя бы дополнять код ответа еще и кодом бизнесовой ошибки в этом случае и желательно ее текстом (error.code и error.message соответственно).
Для валидации желательно тоже).
А для всего остального можно и специальные коды использовать.
P.S.: По традиции - буду признателен за вопросы про карьеру\профессию\чему угодно связанному со сферой IT - постараюсь ответить на всё.
P.P.S.: Также веду телеграмм-канал, в котором делюсь разным про профессию и про свой путь в ней. Есть и хардовая информация (асинхронные, синхронные интеграции, примеры ТЗ\шаблонов написания микросервисов), так и более софтовая - см. закрепленный дайджест.
Комментарии