Какие основные ограничения у REST API?

«Какие основные ограничения у REST API?» — вопрос из категории Сети, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

При проектировании и использовании RESTful API на Node.js я сталкивался со следующими ограничениями:

  1. Over-fetching и Under-fetching:

    • Over-fetching: Клиент получает больше данных, чем нужно. Например, эндпоинт /api/users/123 возвращает все 20 полей пользователя, а клиенту нужно только имя и аватар.
    • Under-fetching: Клиенту не хватает данных, и он вынужден делать несколько последовательных запросов. Чтобы построить профиль страницы, может потребоваться запрос к /users/123, затем к /users/123/posts, потом к /posts/456/comments.
    • Решение в Node.js: Можно использовать библиотеки для частичных ответов (fields query parameter) или рассмотреть GraphQL.
  2. Отсутствие встроенного реального времени (Real-time): REST основан на HTTP-цикле "запрос-ответ". Для чатов, уведомлений или live-обновлений нужны отдельные технологии.

    • Решение в Node.js: Использовать WebSockets (библиотеки Socket.IO, ws) или Server-Sent Events (SSE) поверх HTTP.
  3. Жёсткость эндпоинтов и версионирование: Структура данных и эндпоинты фиксированы. При изменении API (например, переименовании поля) приходится поддерживать старые версии (/api/v1/users, /api/v2/users), что усложняет код.

  4. Сложность описания сложных операций: Иногда операция не укладывается в CRUD (Create, Read, Update, Delete) и HTTP-методы. Например, "отправить письмо", "сбросить пароль". Приходится использовать "действия" в виде POST на вложенный ресурс: POST /api/users/123/reset-password.

  5. Проблемы с производительностью при вложенных данных: Чтобы избежать under-fetching, API может начать возвращать глубоко вложенные объекты, но это приводит к over-fetching и сложностям в поддержке. Выбор между этими крайностями — постоянный компромисс.

В моих проектах на Node.js с Express для борьбы с over-fetching я часто реализовывал поддержку query-параметров fields или select.

// Эндпоинт с поддержкой выбора полей
app.get('/api/users/:id', async (req, res) => {
  const fields = req.query.fields ? req.query.fields.split(',') : [];
  const projection = {};
  if (fields.includes('name')) projection.name = 1;
  if (fields.includes('email')) projection.email = 1;
  // ...
  const user = await User.findById(req.params.id, projection);
  res.json(user);
});
// Запрос: GET /api/users/123?fields=name,email