ћерион Ќетворкс

13 минут

REST API Ц один из самых распространенных типов доступных веб-сервисов, но проектировать их сложно. ќни позвол€ют разным клиентам, включа€ браузер, настольные приложени€, мобильные приложени€ и практически любое устройство с подключением к »нтернету, взаимодействовать с сервером. »менно поэтому очень важно правильно проектировать REST API, чтобы в будущем не было проблем.

15 важнейших рекомендаций по проектированию REST API

—оздание API с нул€ может оказать непосильной задачей из-за большого количества вещей, которые необходимо учесть Ц от базовой безопасности до использовани€ правильных методов HTTP, реализации аутентификации, определени€ того, какие запросы и ответы среди многих других принимаютс€ и возвращаютс€. ¬ этой статье € очень постаралс€ сжать материал в 15 пунктов с важными рекомендаци€ми, которые позвол€т создать хороший API. ¬се рекомендации никак не завис€т от €зыка, поэтому потенциально применимы к любой платформе или технологии.


1. ќб€зательно используйте имена существительные в названи€х пут€х к конечным точкам

¬ам всегда следует использовать имена существительные, которые обозначают объект, который вы извлекаете или которым вы манипулируете. ¬ качестве имени пути всегда предпочтительнее использовать множественное число. »збегайте использовани€ глаголов в названи€х пут€х к конечным точкам, потому что наш метод HTTP-запроса уже €вл€етс€ глаголом и по сути не добавл€ет никакой новой информации.

ƒействие должно быть произведено с помощью методов HTTP-запроса. Ќаиболее распространенными €вл€ютс€ методы GET, POST, PATCH, PUT и DELETE.

  • GET извлекает ресурсы
  • POST отправл€ет новые данные на сервер
  • PUT/PATCH модифицируют уже существующие данные
  • DELETE удал€ет данные

√лаголы сопоставл€ютс€ с функци€ми CRUD (Create, read, update и delete).

ѕомн€ об этих принципах, мы должны создавать маршруты типа GET /books дл€ получени€ списка книг, а не GET /get-books или GET /book. јналогично, POST /books - дл€ добавлени€ новой книги, PUT /books/:id - дл€ модификации полных данных книги с заданным идентификатором (id), а PATCH /books/:id обновл€ет частичные изменени€ в книге. » наконец, DELETE /books/:id предназначен дл€ удалени€ существующей книги в заданным идентификатором.


2. JSON как основной формат отправки и получени€ данных

Ќесколько лет назад прием и ответы на запросы API выполн€лись в основном в XML. Ќо сейчас Ђстандартнымї форматом дл€ отправки и получени€ данных API в большинстве приложений стал JSON. ѕоэтому наш второй пункт рекомендует убедитьс€, что конечные точки возвращают формат данных JSON в качестве ответа, а также при приеме информации через полезную нагрузку HTTP-сообщений.

Ќесмотр€ на то, что FormData хорошо подходит дл€ отправки данных от клиента, особенно если нам нужно отправл€ть файлы, они не очень подход€т дл€ текста и чисел. Ќам не нужны FormData дл€ их передачи, так как в большинстве фреймворков можно передавать JSON непосредственно на стороне клиента. ѕри получении данных от клиента нам необходимо убедитьс€, что клиент правильно интерпретирует данные JSON, и дл€ этого при выполнении запроса в заголовке ответа Content-Type должен быть установлен на application/json.

—тоит еще раз упом€нуть исключение, когда мы пытаемс€ отправл€ть и получать файлы между клиентом и сервером. ¬ этом конкретном случае нам необходимо обрабатывать файл ответа и отправл€ть FormData с клиента на сервер.

3. »спользуйте коды состо€ний HTTP

 оды состо€ний HTTP всегда полезно использовать дл€ того, чтобы указать на выполнение или невыполнение запроса. Ќе используйте слишком много кодов состо€ний и всегда используйте одни и те же коды дл€ одних и тех же результатов в API. ¬от некоторые примеры:

  • 200 Ц общее выполнение
  • 201 Ц успешное создание
  • 400 Ц неверные запросы от клиента, такие как неверные параметры
  • 401 Ц несанкционированные запросы
  • 403 Ц отсутствие прав доступа к ресурсам
  • 404 Ц отсутствуют ресурсы
  • 429 Ц слишком много запросов
  • 5хх Ц внутренние ошибки (их следует избегать насколько это возможно)

¬ зависимости от ситуаций их может быть и больше, но ограничение количества кодов состо€ний помогает клиенту использовать более предсказуемый API.


4. ¬озвращайте стандартизированные сообщени€

ѕомимо использовани€ кодов состо€ни€ HTTP, которые указывают на результат запроса, всегда используйте стандартизированные ответы дл€ аналогичных конечных точек. ѕользователи могут всегда рассчитывать на одинаковую структуру и действовать соответственно. Ёто также относитс€ к статусу, указывающему на выполнение запроса, и сообщени€х об ошибках. ¬ случае выборки коллекций придерживайтесь определенного формата, независимо от того, включает ли тело ответа массив данных, подобный этому:

[
  {
     bookId: 1,
     name: "The Republic"
  },
  {
     bookId: 2,
     name: "Animal Farm"
  }
]

или вот такой комбинированный ответ:

{
   "data": [ 
     {
       "bookId": 1,
       "name": "The Republic"
     },
     {
       "bookId": 2,
       "name": "Animal Farm"
     }
   ],
   "totalDocs": 200,
   "nextPageId": 3
}    

«десь рекомендаци€ заключаетс€ в том, чтобы быть последовательным независимо от того, какой подход вы выберете дл€ этого. јналогичное поведение должно быть реализовано при извлечении объекта, а также при создании и модификации ресурсов, которым обычно рекомендуетс€ возвращать последний экземпл€р объекта.

// ќтвет после успешного вызова POST /books
 {
     "bookId": 3,
     "name": "Brave New World"
 } 

’оть это и никак не навредит, но все же излишнем будет включать универсальное сообщение, например, Ђ нига успешно созданаї, так как это уже следует из кода состо€ни€ HTTP.

» последнее, но не менее важное: при наличии стандартного формата ответа коды ошибок также важны (и даже более важные). Ёто сообщение должно включать информацию, которую клиент может использовать дл€ представлени€ ошибок конечному пользователю, а соответственно, это должно быть не общее предупреждение, такое как Ђто-то пошло не такї, которого следует избегать, насколько это возможно. ¬от пример:

{
  "code": "book/not_found",
  "message": "A book with the ID 6 could not be found"
}

ќп€ть же, нет необходимости включать код состо€ни€ в содержимое ответа, но полезно определить набор кодов ошибок, таких как book/not_found, чтобы пользователь мог сопоставить их с разными строками и создать свое собственное сообщение об ошибке дл€ конечного пользовател€. ¬ частности, дл€ сред разработки или промежуточных сред может показатьс€ правильным также включить стек ошибок в ответ с целью помочь в отладке ошибок. Ќо не включайте те, что наход€тс€ в промышленной эксплуатации, так как это создаст угрозу безопасности, раскрыва€ незапланированную информацию.


5. »спользуйте разбиение на страницы, фильтрацию и сортировку при выборе коллекций записей

 ак только будет создана конечна€ точка, котора€ возвращает список элементов, необходимо будет установить разбиение на страницы. ќбычно коллекции со временем растут, поэтому важно всегда следить за тем, чтобы возвращалось ограниченное и контролируемое количество элементов. —праведливо будет позволить пользовател€м API выбирать, сколько объектов получить, но всегда полезно заранее определить число и установить дл€ него максимум. ќсновна€ причина, почему нужно это сделать, заключаетс€ в том, что дл€ возврата огромного массива данных потребуетс€ очень много времени и больша€ пропускна€ способность.

ƒл€ реализации нумерации страниц есть два хорошо известных способа: skip/limit или keyset. ѕервый вариант обеспечивает более удобный дл€ пользовател€ способ извлечени€ данных, но обычно он менее эффективен, так как базы данных сканируют множество документов дл€ извлечени€ нужных записей. ћне больше нравитс€ второй вариант. –азделени€ на страницы с помощью keyset получает идентификатор (id) в качестве ссылки дл€ Ђвырезани€ї коллекции или таблицы с условием без сканировани€ записей.

“акже API должны предоставл€ть фильтры и возможности сортировки, которые упрощают способы получени€ данных. „астью решени€ повышени€ производительности €вл€ютс€ индексные базы данных, которые позвол€ют максимизировать производительность при помощи шаблонов доступа, которые примен€ютс€ с фильтрами и параметрами сортировки.

ѕри проектировании API эти свойства разбиени€ на страницы, фильтрации и сортировки определ€ютс€ как параметры запроса в URL-адресе. Ќапример, если вы хотим получить информацию о первых 10 книгах, принадлежащих к категории Ђроманї, то наша конечна€ точка будет выгл€деть вот так:

GET /books?limit=10&category=romance

6. PATCH вместо PUT

ћаловеро€тно, что необходимо будет сразу полностью обновить всю запись, обычно есть конфиденциальные или полные записи, которые следует уберечь от манипул€ций пользовател€. »менно поэтому дл€ выполнени€ частичных обновлений ресурса следует использовать PATCH, а вот PUT полностью мен€ет существующий ресурс. ќни оба должны использовать тело запроса дл€ передачи информации, подлежащей модификации. –азница лишь в том, что дл€ PATCH это пол€, а дл€ запроса PUT Ц полный объект. “ем не менее, стоит отметить, что ничто не мешает нам использовать PUT дл€ частичной модификации, нет никаких Ђограничений на передачу по сетиї, которые бы это подтверждали. Ёто просто факт, которого стоит придерживатьс€.


7. ѕредоставьте более подробные ответы

Ўаблоны доступа €вл€ютс€ ключевыми при создании доступных ресурсов API и возвращаемых данных.  огда система растет, то и свойства записи также растут, но не всегда все эти свойства нужны клиентам дл€ работы. »менно в таких ситуаци€х становитс€ полезным предоставление возможности возвращать сокращенные или полные ответы дл€ одной и той же конечной точки. ≈сли пользователю нужны только некоторые пол€, то упрощенный ответ помогает снизить расход трафика и потенциально сложность получени€ других вычисл€емых полей.

ѕростой способ реализовать Ц предоставить дополнительный параметр запроса, чтобы включить или отключить предоставление более подробного ответа.

GET /books/:id
{
   "bookId": 1,
   "name": "The Republic"
}GET /books/:id?extended=true
{
   "bookId": 1,
   "name": "The Republic"
   "tags": ["philosophy", "history", "Greece"],
   "author": {
      "id": 1,
      "name": "Plato"
   }
}

8. ќб€занность конечной точки

ѕринцип единственной об€занности фокусируетс€ на концепции удержани€ функции, метода или класса на одной об€занности, которую они выполн€ют хорошо. ћы можем сказать, что это наш API - хороший API, если он выполн€ет одну конкретную вещь и никогда не мен€етс€. Ёто помогает пользовател€м лучше пон€ть наш API и сделать его более предсказуемым, что облегчит общую интеграцию. Ћучше всего расширить список доступных конечных точек, а не создавать очень сложные конечные точки, которые пытаютс€ решить множество задач одновременно.


9. ѕредоставьте полную документацию по API

ѕользователи вашего API должны понимать, как использовать доступные конечные точки и чего ожидать. Ёто возможно только при наличии хорошей и подробной документации. ќбратите внимание на следующие аспекты, чтобы ваша документаци€ была полной.

  • ƒоступные конечные точки с описанием их назначени€
  • ѕрава доступа, необходимые дл€ выполнени€ конечной точки
  • ѕримеры вызовов и ответов
  • —ообщени€ о предполагаемых ошибках

Ќемаловажным €вл€етс€ посто€нное обновление документации после внесени€ изменений и дополнений в систему. Ћучший способ дл€ этого Ц сделать документацию по API неотъемлемой частью разработки. ƒвум€ хорошо известными инструментами в данном вопросе €вл€ютс€ Swagger и Postman Ц они доступны дл€ большинства сред разработки API.


10. »спользуйте SSL дл€ обеспечени€ безопасности и настройте CORS

Ѕезопасность Ц еще одно очень важной свойство, которым должен обладать наш API. Ќастройка SSL путем установки действительного сертификата на сервер обеспечит безопасную св€зь с пользовател€ми и предотвратит некоторые виды потенциальных атак.

CORS (Cross-origin resource sharing Ц ќбмен ресурсами с запросом происхождени€) Ц это функци€ безопасности браузера, котора€ ограничивает HTTP-запросы из различных источников, которые инициируютс€ сценари€ми, запущенными в браузере. ≈сли ресурсы вашего REST API получают непростые HTTP-запросы из разных источников, то вам нужно включить поддержку CORS дл€ того, чтобы пользователи работали соответствующим образом.

ѕротокол CORS требует, чтобы браузер отправил предварительный запрос на сервер и дождалс€ утверждени€ (или запрос учетных данных) с сервера перед отправкой фактического запроса. «апрос предварительной проверки отображаетс€ в API как HTTP-запрос, использующий метод OPTIONS (среди других заголовков). «начит, дл€ поддержки CORS в ресурсе REST API необходимо реализовать метод OPTIONS, который будет отвечать на предварительный запрос, по крайней мере, со следующими заголовками ответа, предусмотренными стандартом Fetch:

  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Origin

 акие значени€ назначать этим ключам, зависит от того, настолько открытым и гибким должен быть наш API. ћы можем назначить определЄнные методы и известные источники или использовать специальные символы, чтобы иметь открытые ограничени€ CORS.


11. ”правление верси€ми API

¬ процессе разработки конечные точки начинают мен€тьс€ и перестраиватьс€. Ќо мы должны, насколько это возможно, избегать внезапного изменени€ конечных точек дл€ пользовател€. –екомендуетс€ рассматривать API как ресурс с обратной совместимость, в котором новые и обновленные конечные точки должны быть доступны, но не должны вли€ть на предыдущие стандарты.

¬от где управление верси€ми API приходит на помощь Ц когда клиенты должны иметь возможность выбирать, к какой версии подключатьс€. ≈сть несколько способов описать управление верси€ми API:

  1. ƒобавление нового заголовка x-version=v2
  2. Ќаличие параметра запроса ?apiVersion=2
  3. ¬ерси€ как часть URL: /v2/books/:id</li>

12.  эшируйте данные дл€ повышени€ производительности

„тобы повысить производительность нашего API, полезно следить за данными, которые редко мен€ютс€ и к которым часто обращаютс€. ƒл€ таких данных мы можем рассмотреть возможность использовани€ базы данных в пам€ти или кэш-пам€ти, котора€ избавит от доступа к основной базе данных. √лавна€ проблема здесь заключаетс€ в том, что данные могут устареть, поэтому следует решить вопрос с внедрением последней версии.

»спользование кэшированных данных будет полезным дл€ пользователей дл€ загрузки конфигураций и каталогов информации, которые не предназначены дл€ посто€нного изменени€ в течение долгого времени. ѕри использовании кэшировани€ не забудьте включить Cache-Control в заголовки. Ёто поможет пользовател€м эффективно использовать систему кэшировани€.


13. »спользуйте даты в формате UTC

—ложно представить системы, которые в какой-то момент перестает работать из-за дат. Ќа уровне данных важно быть логичным в том, как даты отображаютс€ на клиентских приложени€х. ISO 8601 Ц это международный стандартный формат данных дл€ даты и времени. ƒанные должны быть в формате Z или UTC, дл€ которых пользователи могут могли бы выбрать часовой по€с в случае, если така€ дата должны отображатьс€ при любых услови€х. ¬от пример того, как должны выгл€деть даты:

{
    "createdAt": "2022-03-08T19:15:08Z"
}

14.  онечна€ точка проверки работоспособности

ћожет произойти ситуаци€, когда наш API перестанет работать, и дл€ его запуска потребуетс€ врем€. ѕри таких обсто€тельствах клиенты хот€т знать, что службы недоступны, и быть в курсе ситуации. ƒл€ этого предоставьте конечную точку (например, GET /health), котора€ бы определ€ла работоспособность API. Ёта конечна€ точка может вызыватьс€ и другими приложени€ми, такими как балансировщики нагрузки. ћожно продвинутьс€ еще дальне и сообщать о периодах технического обслуживани€ или работоспособности частей API.


15. –азрешите аутентификацию по ключу API

јутентификаци€ с помощью ключей API даст возможность сторонним приложени€м легко создавать интеграцию с нашим API. Ёти ключи API следует передавать с помощью пользовательского заголовка HTTP (например, Api-Key или X-Api-Key).  лючи должны иметь дату окончани€ срока действи€, и должна быть возможность их отозвать с целью признани€ недействительными по соображени€м безопасности.


—кидки 50% в Merion Academy

¬ыбрать курс