Skip to content

Instantly share code, notes, and snippets.

@imevro
Last active February 12, 2023 11:59
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save imevro/25e6cd006bb045557eeef92ea4472c88 to your computer and use it in GitHub Desktop.
Save imevro/25e6cd006bb045557eeef92ea4472c88 to your computer and use it in GitHub Desktop.
Дизайн REST API — основан на 4-летнем опыте работы

Based on RESTful API Design — Step By Step Guide

Основное, на что нужно обратить внимание:

Держать простым

Не забывать про методы

  • GET запрашивает данные
  • POST отправляет чтобы создать
  • PUT отправляет чтобы обновить
  • DELETE удаляет

Использовать корректные статус-коды в ответах

Будет проще отрабатывать ответ и меньше работы: вместо ошибки { error: 'Сервер упал, к сожалению' } достаточно послать код 503

Ресурсы как существительное, не глагол

У нас же есть методы!

Использовать множественное значение

И принцип от общего к частному: сначала продукты, потом — конкретный продукт.

Использовать пагинацию, а не выдавать всё разом

Самая удобная пагинация — на оффсетах (когда через квери-параметры передаётся offset и limit)

В коллекциях возвращать информацию о пагинации

В коллекциях удобно иметь структуру

{
  items: Array<{MyModel}>,
  pagination: {
    offset: Number = 0,
    limit: Number = 32,
    total: Number,
  }
}

Использовать квери-параметры для GET запросов, если нужно сузить выборку

Примеры квери-параметров

По моему субъективному опыту в несколько лет, самый удобный формат таков:

Пагинация

Через limit=&offset=&

Сортировка

Через orderBy[key]={asc,desc} (orderBy[name]=asc или orderBy[id]=desc)

Фильтры

  • фильтры через префикс filter[] и ключ (например, filter[id], filter[name], filter[price])
  • по строке через *: filter[name]=*esl* для поиска Tesla
  • несколько значений через ,: filter[id]=1,2,3,4
  • диапазон через .., где оба поля опциональны: filter[price]=..100 (до 100), filter[price]=200.. (от 200), filter[price]=50..500 (от 50 до 500)
  • булеан-значения в прямом виде: filter[isResale]=true
  • вложенные поля через точку: filter[location.settlementId]=134
  • filterNot для обратного поведения (filterNot[isDisabled]=true: вернуть все объекты, где isDisabled не стоит в true — удобно, если isDisabled необязательное поле и может быть как и null, так и false)

📌 tip: такие фильтры реализовываются через Elastic и Elasticsearch

Использовать версионирование

Даже если это внутренний API — меньше когнитивная нагрузка на «сломали ли АПИ или будет работать?». Раз стоит версия, значит разработчик пообещал что она будет стабильна.

Использовать JSON и обязательно через хедер accept: application/json

АПИ должен явно задавать через хедер accept что он принимает mime-type application/json и никакой больше. 📌 Исключение: файлы всё ещё удобнее заливать через multipart/form-data — тут велосипедов строить не нужно

Клиент должен передавать заголовок content-type с тем mime-type, который он хочет послать. Сервер явно должен обрабатывать этот хедер и слать ошибку, если он не принимает этот content-type.

Давать чёткие ошибки

Ошибки дают понимание конкретной проблемы

Не использовать общий нейминг

Типа items, products, elements.

Если возвращаете список автомобилей — ресурс /cars, если производителей — /manufacturers, мест — /places.

Документировать

Желательно через Сваггер

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment