Зарегистрируйтесь, чтобы продолжить обучение

REST-архитектура JS: REST API (Fastify)

Термин REST был введен в обиход Роем Филдингом в 2000 году в его докторской диссертации. Через него Рой определял как можно строить взаимодействие между сервисами в сети, где между сервисами нет прямой связи и они могут работать на любых технологиях.

REST это широкое понятие, которое говорит нам о том, каких принципов стоит придерживаться, чтобы наше API обладали определенными свойствами, но не говорит о том, как конкретно их реализовать. Поэтому REST называют архитектурным стилем для построения API и часто противопоставляют RPC (Remote Procedure Call) подходу, в котором HTTP используется как транспорт, а вся смысловая часть зашита в передаваемое сообщение. Само сообщение, в RPC API, эмулирует вызов функции, но по сети. REST же использует HTTP по своему прямому назначению с кодами ответов, правильными заголовками, механизмами кеширования и тому подобному.

API построенные на принципах REST называют RESTful API, хотя чаще это RESTlike API, потому что придерживаться принципов REST на 100% практически невозможно и на практике не нужно, в чем вы скоро убедитесь.

Пример: Github REST API

Перед тем как нырять в особенности REST, посмотрим один из самых популярных примеров, когда речь заходит за эталонный REST API в интернете.

GET /users/{username}/repos

Этот запрос возвращает список репозиториев, принадлежащих пользователю {username}.

GET https://api.github.com/users/hexlet/repos

Ответ

[
  {
    "id": 1296269,
    "name": "hexlet",
    "full_name": "hexlet/hexlet",
    "private": false,
    "html_url": "https://github.com/hexlet/hexlet"
  },
  { ... }
]

/repos/{owner}/{repo}/issues

Этот запрос возвращает список ишьюсов для репозитория {owner}/{repo}.

GET https://api.github.com/repos/hexlet/hexlet/issues

Ответ

[
  {
    "id": 1,
    "title": "Found a bug",
    "number": 1347,
    "state": "open",
    "url": "https://api.github.com/repos/hexlet/hexlet/issues/1347"
  },
  { ... }
]

Что дает и не дает нам REST?

Что мы получаем, строя API в соответствии с принципами REST?

  • REST API построен на слабой связанности между клиентом и сервером, что позволяет им развиваться независимо друг от друга. REST опирается на стандартные методы HTTP и универсальные ресурсы, что делает его менее зависимым от конкретных реализаций.
  • REST API использует стандарты HTTP, такие, как методы GET, POST, PUT, DELETE, которые широко используются и хорошо известны разработчикам. Это упрощает понимание и использование REST API. Например, легко реализовать кеширование.
  • URI в REST API представляют собой понятные, человекочитаемые ресурсы. Например, GET /users/123 возвращает данные пользователя с ID 123, что сразу понятно разработчикам. К тому, же, позволяет без проблем пользоваться знакомыми всем инструментами типа curl.
  • REST API может эволюционировать и изменяться без нарушения совместимости с существующими клиентами. Например, можно добавлять новые поля в ответ или новые маршруты без изменения уже существующих.
  • Благодаря широкому распространению и стандартам, REST API легко интегрируется с различными клиентами и инструментами. Многие библиотеки и фреймворки поддерживают работу с REST из коробки.

Все эти преимущества действительно работают, когда мы говорим про публичное API, выставляемое для пользователей нашего сервиса вне контура компании. Но у REST API есть и недостатки, которые делают его использование между сервисами внутри компании не эффективным.

Во-первых, REST API, основанное на HTTP, может быть избыточным в тех случаях, когда требуется минимальная задержка и высокая производительность, поскольку каждый запрос несет в себе значительное количество метаданных (заголовки, URI), что увеличивает объем передаваемых данных. Внутри компании, где взаимодействие сервисов часто происходит в высоконагруженных средах, это может приводить к дополнительной нагрузке на сеть и увеличению задержек.

Во-вторых, слабая связанность REST API иногда усложняет реализацию транзакций, состоящих из нескольких шагов, которые необходимо выполнять атомарно. Внутри компании, где требуется более тесная координация действий между сервисами, RPC или другие более строго типизированные и специализированные протоколы могут оказаться более подходящими, обеспечивая более надежное выполнение транзакционных операций.

Наконец, REST API, особенно в сложных системах с множеством ресурсов и операций, может стать трудным для поддержания, если не внедрены четкие правила версионирования и документирования. Это может привести к проблемам в масштабировании и эволюции системы внутри компании, где изменения происходят быстро и требуют высокой степени согласованности между сервисами.

Таким образом, несмотря на все преимущества REST для публичных интерфейсов, внутри корпоративных систем часто предпочитают использовать другие подходы, такие как tRPC, gRPC или GraphQL, которые лучше подходят для внутренних нужд и специфических требований взаимодействия между сервисами.

Что сейчас подразумевается под REST?

Когда Рой Филдинг предложил концепцию REST, он заложил основы для архитектурного стиля, который должен был обеспечивать гибкость, масштабируемость и независимость компонентов в распределенных системах. Одним из ключевых элементов этой концепции является HATEOAS (Hypermedia as the Engine of Application State) — принцип, согласно которому каждый ответ API должен содержать ссылки (гипермедиа), позволяющие клиенту динамически взаимодействовать с ресурсами без необходимости предварительного знания API. В идеальном RESTful API клиент должен понимать и использовать API исключительно на основе гипермедиа, получаемой от сервера. Пример эталонного ответа по REST включающий в себя ссылки на другие ресурсы:

{
    "account": {
        "account_number": 12345,
        "balance": {
            "currency": "usd",
            "value": 100.00
        },
        "links": {
            "deposits": "/accounts/12345/deposits",
            "withdrawals": "/accounts/12345/withdrawals",
            "transfers": "/accounts/12345/transfers",
            "close-requests": "/accounts/12345/close-requests"
        }
    }
}

Однако на практике идеи Филдинга не всегда приживаются полностью, и большинство современных REST API далеки от оригинальных принципов REST, особенно в части HATEOAS. Многие разработчики считают, что полное соблюдение HATEOAS слишком усложняет разработку и не приносит значительных преимуществ в реальных проектах.

Современное использование термина REST в большей степени связано с более практичным подходом, который включает в себя:

Ресурсо-ориентированность

В REST API основными объектами взаимодействия являются ресурсы. Каждый ресурс идентифицируется уникальным URI, и операции над ресурсами выполняются с использованием стандартных методов HTTP (GET, POST, PUT, DELETE и т.д.).

Предположим, у нас есть сервис для управления пользователями. В этом случае ресурсы могут быть следующими:

  • GET /users — Получить список всех пользователей.
  • GET /users/123 — Получить информацию о пользователе с ID 123.
  • POST /users — Создать нового пользователя.
  • PUT /users/123 — Обновить информацию о пользователе с ID 123.
  • DELETE /users/123 — Удалить пользователя с ID 123.

Ресурсы в URI должны быть существительными, отражающими сущности, а не действия, как в RPC.

Еще один пример это адрес урока, который вы проходите прямо сейчас на Хекслете. Посмотрите в адресную строку браузера и попробуйте увидеть там ресурс и идентификатор.

Идемпотентность методов

Идемпотентность означает, что многократное выполнение одного и того же запроса приводит к одному и тому же результату. Это важно для обеспечения предсказуемости поведения API. Идемпотентность в REST полагается на стандарт HTTP, где описано какие методы должны быть идемпонтенты и что это значит. Например:

  • PUT /users/123 — Обновление информации о пользователе с ID 123. Если отправить один и тот же запрос несколько раз, результат будет одинаковым — данные пользователя будут обновлены.
  • DELETE /users/123 — Удаление пользователя с ID 123. Независимо от того, сколько раз вы отправляете этот запрос, результат будет один и тот же — пользователь будет удален (если он существует), а последующие запросы вернут информацию о том, что пользователя больше нет.

Кэширование

REST API активно использует возможности HTTP для кэширования данных, чтобы снизить нагрузку на сервер и ускорить доступ к ресурсам. Это реализуется с помощью заголовков HTTP, таких как Cache-Control, ETag, Last-Modified, которые позволяют клиентам и серверам эффективно управлять кэшированием. Предположим, что наш API предоставляет данные о пользователях:

  • GET /users/123 с заголовком Cache-Control: max-age=3600 указывает клиенту кэшировать ответ на 1 час. Это значит, что при повторном запросе в течение этого времени клиент будет использовать закэшированные данные, а не отправлять запрос на сервер.
  • ETag используется для проверки, изменился ли ресурс. Например, сервер может вернуть заголовок ETag: "12345" вместе с ответом на GET-запрос. При следующем запросе клиент может отправить заголовок If-None-Match: "12345", и если данные не изменились, сервер вернет 304 Not Modified без тела ответа, что уменьшает трафик.

Отсутствие жесткой связанности между клиентом и сервером

Что это значит: REST API обеспечивает слабую связанность, что позволяет клиентам и серверам развиваться независимо друг от друга. Они взаимодействуют через стандартизированные протоколы (например, HTTP) и форматы данных (например, JSON), что упрощает интеграцию и масштабирование. Пример:

Клиент отправляет запрос GET /users/123, чтобы получить данные о пользователе в формате JSON. Сервер может обновлять и изменять внутреннюю структуру данных, не затрагивая клиента, поскольку клиент работает только с JSON-ответом, а не с внутренними данными сервера.

Это также позволяет разным клиентам (веб-приложениям, мобильным приложениям) использовать один и тот же API, получая данные в едином формате.

Эволюция API и совместимость

REST API может меняться и расширяться без нарушения работы существующих клиентов. Это достигается за счет добавления новых ресурсов, маршрутов или полей в ответы, без изменения существующих.

Допустим, API возвращает информацию о пользователе в формате JSON:

{
  "id": 123,
  "name": "John Doe",
  "email": "john.doe@example.com"
}

В будущем вы добавляете поле phone, но при этом старые клиенты продолжат работать, так как они просто игнорируют новое поле. Эта концепция называется открытая схема:

{
  "id": 123,
  "name": "John Doe",
  "email": "john.doe@example.com",
  "phone": "+1234567890"
}

Для значительных изменений может использоваться версионирование API (например, через заголовки или URL /v2/users/123), что позволяет плавно обновлять клиентов.

Как проектировать REST API?

Правила описанные выше, описывают базовые условия, которым должны соответствовать API, стремящиеся получить преимущества REST архитектуры. Но как конкретно организовывать маршруты, структуру входных и выходных данных, коды ответов, описание ошибок? Единого стандарта не существует и многие компании вырабатывают собственные правила и принципы. Некоторые из них достаточно популярны, кое-что есть в спецификации и было несколько попыток создать универсальный стандарт. Вот на что можно ориентироваться при проектировании REST API, ну или как минимум подсматривать:

  • Problem Details for HTTP APIs
  • jsonapi
  • odata
  • HAL
  • Collection+JSON
  • Siren
  • Hydra

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff