API Design Patterns: RESTful, RPC, Hypermedia

← Вернуться к статьям

Введение

Выбор правильного паттерна проектирования API критически важен для успеха проекта. Разные паттерны подходят для разных задач: RESTful для ресурс-ориентированных API, RPC для процедурных вызовов, Hypermedia для самодокументируемых API.

В этом руководстве мы рассмотрим три основных паттерна проектирования API: RESTful, RPC и Hypermedia (HATEOAS), их преимущества, недостатки, когда использовать каждый из них и практические примеры реализации.

✅ Что вы узнаете:

💡 Что такое паттерн проектирования API?

Паттерн проектирования API — это архитектурный подход к организации взаимодействия между клиентом и сервером. Каждый паттерн имеет свои принципы, преимущества и области применения.

📋 Содержание

1. RESTful Patterns 🌐

REST (Representational State Transfer) — архитектурный стиль для распределённых систем, основанный на использовании HTTP методов и ресурс-ориентированном подходе.

Принципы REST

Основные принципы REST:

Пример RESTful API

# Получить список пользователей
GET /api/users
# Response: [{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"}]

# Получить конкретного пользователя
GET /api/users/1
# Response: {"id": 1, "name": "John", "email": "john@example.com"}

# Создать нового пользователя
POST /api/users
Content-Type: application/json
{"name": "Bob", "email": "bob@example.com"}
# Response: {"id": 3, "name": "Bob", "email": "bob@example.com"}

# Обновить пользователя
PUT /api/users/1
Content-Type: application/json
{"name": "John Updated", "email": "john@example.com"}
# Response: {"id": 1, "name": "John Updated", "email": "john@example.com"}

# Удалить пользователя
DELETE /api/users/1
# Response: 204 No Content

# Вложенные ресурсы
GET /api/users/1/posts
# Response: [{"id": 1, "title": "Post 1"}, {"id": 2, "title": "Post 2"}]

Реализация RESTful API в Node.js

// Express.js RESTful API
const express = require('express');
const app = express();

app.use(express.json());

// GET /api/users - список пользователей
app.get('/api/users', async (req, res) => {
  const users = await db.getUsers();
  res.json(users);
});

// GET /api/users/:id - получить пользователя
app.get('/api/users/:id', async (req, res) => {
  const user = await db.getUser(req.params.id);
  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }
  res.json(user);
});

// POST /api/users - создать пользователя
app.post('/api/users', async (req, res) => {
  const user = await db.createUser(req.body);
  res.status(201).json(user);
});

// PUT /api/users/:id - обновить пользователя
app.put('/api/users/:id', async (req, res) => {
  const user = await db.updateUser(req.params.id, req.body);
  res.json(user);
});

// DELETE /api/users/:id - удалить пользователя
app.delete('/api/users/:id', async (req, res) => {
  await db.deleteUser(req.params.id);
  res.status(204).send();
});
✅ Преимущества REST:
❌ Недостатки REST:

2. RPC Style API 🔧

RPC (Remote Procedure Call) — стиль API, где операции представлены как вызовы функций. Клиент вызывает удалённые процедуры, как если бы они были локальными функциями.

Характеристики RPC API

Особенности RPC:

Пример RPC API

# Выполнить действие
POST /api/calculateSum
Content-Type: application/json
{"a": 5, "b": 3}
# Response: {"result": 8}

# Отправить email
POST /api/sendEmail
Content-Type: application/json
{"to": "user@example.com", "subject": "Hello", "body": "Message"}
# Response: {"status": "sent", "messageId": "123"}

# Получить статистику
POST /api/getStatistics
Content-Type: application/json
{"userId": 1, "period": "week"}
# Response: {"views": 150, "clicks": 45}

# Выполнить сложную операцию
POST /api/processOrder
Content-Type: application/json
{"orderId": 123, "action": "refund"}
# Response: {"status": "refunded", "amount": 99.99}

Реализация RPC API

// Express.js RPC API
const express = require('express');
const app = express();

app.use(express.json());

// RPC endpoint для выполнения действий
app.post('/api/:action', async (req, res) => {
  const { action } = req.params;
  const params = req.body;

  try {
    switch (action) {
      case 'calculateSum':
        const result = params.a + params.b;
        return res.json({ result });

      case 'sendEmail':
        await emailService.send(params.to, params.subject, params.body);
        return res.json({ status: 'sent' });

      case 'getStatistics':
        const stats = await statsService.get(params.userId, params.period);
        return res.json(stats);

      case 'processOrder':
        const order = await orderService.process(params.orderId, params.action);
        return res.json(order);

      default:
        return res.status(404).json({ error: 'Action not found' });
    }
  } catch (error) {
    return res.status(500).json({ error: error.message });
  }
});

// Альтернативный подход: отдельные endpoints для каждой операции
app.post('/api/calculateSum', async (req, res) => {
  const { a, b } = req.body;
  res.json({ result: a + b });
});

app.post('/api/sendEmail', async (req, res) => {
  await emailService.send(req.body.to, req.body.subject, req.body.body);
  res.json({ status: 'sent' });
});
✅ Преимущества RPC:
❌ Недостатки RPC:

3. Hypermedia (HATEOAS) 🔗

Hypermedia (HATEOAS - Hypermedia as the Engine of Application State) — подход, при котором API возвращает не только данные, но и ссылки на доступные действия. Клиент следует этим ссылкам для навигации по API, делая API самодокументируемым.

Принципы HATEOAS

Основные принципы:

Пример Hypermedia API

# GET /api/users/1
{
  "id": 1,
  "name": "John",
  "email": "john@example.com",
  "_links": {
    "self": { "href": "/api/users/1" },
    "update": { "href": "/api/users/1", "method": "PUT" },
    "delete": { "href": "/api/users/1", "method": "DELETE" },
    "posts": { "href": "/api/users/1/posts" },
    "collection": { "href": "/api/users" }
  }
}

# GET /api/users
{
  "users": [
    {
      "id": 1,
      "name": "John",
      "_links": {
        "self": { "href": "/api/users/1" }
      }
    },
    {
      "id": 2,
      "name": "Jane",
      "_links": {
        "self": { "href": "/api/users/2" }
      }
    }
  ],
  "_links": {
    "self": { "href": "/api/users" },
    "create": { "href": "/api/users", "method": "POST" }
  }
}

# GET /api/orders/123
{
  "id": 123,
  "status": "pending",
  "total": 99.99,
  "_links": {
    "self": { "href": "/api/orders/123" },
    "cancel": { "href": "/api/orders/123/cancel", "method": "POST" },
    "pay": { "href": "/api/orders/123/pay", "method": "POST" }
  },
  "_actions": {
    "cancel": {
      "available": true,
      "description": "Cancel this order"
    },
    "pay": {
      "available": true,
      "description": "Pay for this order",
      "method": "POST"
    },
    "refund": {
      "available": false,
      "description": "Cannot refund until order is paid"
    }
  }
}

Реализация Hypermedia API

// Express.js Hypermedia API
const express = require('express');
const app = express();

function addLinks(resource, req) {
  const baseUrl = `${req.protocol}://${req.get('host')}`;
  
  resource._links = {
    self: {
      href: `${baseUrl}${req.originalUrl}`
    }
  };
  
  return resource;
}

app.get('/api/users/:id', async (req, res) => {
  const user = await db.getUser(req.params.id);
  const baseUrl = `${req.protocol}://${req.get('host')}`;
  
  user._links = {
    self: { href: `${baseUrl}/api/users/${user.id}` },
    update: {
      href: `${baseUrl}/api/users/${user.id}`,
      method: 'PUT'
    },
    delete: {
      href: `${baseUrl}/api/users/${user.id}`,
      method: 'DELETE'
    },
    posts: {
      href: `${baseUrl}/api/users/${user.id}/posts`
    },
    collection: {
      href: `${baseUrl}/api/users`
    }
  };
  
  res.json(user);
});

app.get('/api/users', async (req, res) => {
  const users = await db.getUsers();
  const baseUrl = `${req.protocol}://${req.get('host')}`;
  
  const response = {
    users: users.map(user => ({
      ...user,
      _links: {
        self: { href: `${baseUrl}/api/users/${user.id}` }
      }
    })),
    _links: {
      self: { href: `${baseUrl}/api/users` },
      create: {
        href: `${baseUrl}/api/users`,
        method: 'POST'
      }
    }
  };
  
  res.json(response);
});

Форматы Hypermedia

Популярные форматы:

✅ Преимущества Hypermedia:
❌ Недостатки Hypermedia:

Сравнение паттернов 📊

Критерий RESTful RPC Hypermedia
Фокус Ресурсы Действия Ресурсы + ссылки
URL структура Ресурсы (/users/1) Действия (/calculateSum) Ресурсы с ссылками
HTTP методы GET, POST, PUT, DELETE Обычно POST GET, POST, PUT, DELETE
Кэширование ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐
Простота ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
Гибкость ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
Стандартизация ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐
Самодокументируемость ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐

Когда какой паттерн использовать? 🎯

✅ Используйте RESTful когда:

✅ Используйте RPC когда:

✅ Используйте Hypermedia когда:

Гибридные подходы 🔀

Часто используется комбинация паттернов: RESTful для основных операций и RPC для специфичных действий.

# RESTful для ресурсов
GET /api/users
POST /api/users
PUT /api/users/1
DELETE /api/users/1

# RPC для специфичных операций
POST /api/users/1/sendEmail
POST /api/users/1/activate
POST /api/users/1/resetPassword

Best Practices 🌟

✅ RESTful Best Practices:

✅ RPC Best Practices:

✅ Hypermedia Best Practices:

Заключение

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

💡 Ключевые выводы:

💡 Совет:

Начните с RESTful для базовых операций, добавьте RPC endpoints для специфичных действий и рассмотрите Hypermedia для сложных API с множеством переходов состояний.

Создайте Mock API за 2 минуты

Независимо от выбранного паттерна проектирования, вы можете создать Mock API с LightBox API и протестировать различные подходы без написания backend кода. Поддерживает REST, RPC и все популярные форматы.

Попробовать бесплатно →
← Вернуться к статьям