Принципы REST
REST — стиль проектирования API: ресурсы, единый набор методов, предсказуемые url.
«REST — это договорённость: ресурсы — существительные, методы HTTP — глаголы над ними.»
До этого мы делали отдельные маршруты как придётся. REST — это набор соглашений, который превращает хаос эндпоинтов в стройную систему. Освоив его, ты будешь проектировать API, понятные любому разработчику с первого взгляда. В этом уроке разберём принципы REST и спроектируем ресурс на их основе.
Ресурсы и методы
В REST всё крутится вокруг ресурсов — сущностей вроде пользователей, статей, заказов. Url именует ресурс (существительное), а HTTP-метод задаёт действие над ним (глагол). Не нужно придумывать /getUser или /deleteUser — действие уже в методе.
| Метод и путь | Действие |
|---|---|
| GET /users | список пользователей |
| GET /users/:id | один пользователь |
| POST /users | создать пользователя |
| PUT /users/:id | заменить целиком |
| PATCH /users/:id | обновить частично |
| DELETE /users/:id | удалить |
Карта REST-маршрутизации
/users
GET ----------------> список
POST ---------------> создать (201)
/users/:id
GET ----------------> получить один (или 404)
PUT ----------------> заменить
PATCH --------------> частично обновить
DELETE -------------> удалить (204)Сопоставление запроса и действия
Смоделируем ядро REST-роутера в браузере: по методу и шаблону пути он выбирает действие. Это объясняет, как Express различает шесть операций над одним ресурсом:
function resolve(method, path) {
const isCollection = path === '/users';
const itemMatch = path.match(/^\/users\/(\w+)$/);
if (isCollection && method === 'GET') return 'список';
if (isCollection && method === 'POST') return 'создать';
if (itemMatch && method === 'GET') return 'получить #' + itemMatch[1];
if (itemMatch && method === 'DELETE') return 'удалить #' + itemMatch[1];
return '405 метод не поддержан';
}
console.log(resolve('GET', '/users')); // список
console.log(resolve('POST', '/users')); // создать
console.log(resolve('GET', '/users/42')); // получить #42
console.log(resolve('DELETE', '/users/42')); // удалить #42Как работает под капотом
REST опирается на семантику HTTP: GET безопасен и кэшируем, PUT и DELETE идемпотентны (повтор не меняет результат), POST — нет. Эти свойства не магия Express, а соглашения, которые ты сам должен соблюдать. Если их нарушить (например, менять данные через GET), сломаются кэширование, повторные запросы и ожидания клиентов.
Частые ошибки
- Глаголы в url.
/createUserвместоPOST /users— это не REST. Действие уже в методе. - Несогласованные имена. То
/user, то/users. Выбери множественное число и держись его. - Игнорировать идемпотентность. PUT и DELETE должны давать одинаковый результат при повторе.
Best practices
- Именуй коллекции существительными во множественном числе:
/users,/orders. - Вложенность для связей:
/users/:id/orders— заказы конкретного пользователя. - Возвращай статусы по смыслу: 201 при создании, 204 при удалении, 404 при отсутствии.
Итоги
REST — это соглашения: ресурсы-существительные в url и методы-глаголы HTTP. Они делают API предсказуемым. Дальше реализуем полный CRUD-набор маршрутов для ресурса и наведём порядок с помощью Router.
Версионирование и формат ответа
Реальное API живёт годами и меняется, поэтому к принципам REST добавляют два практических соглашения. Первое — версионирование: контракт фиксируют в пути (/v1/users) или в заголовке, чтобы новые изменения не ломали старых клиентов. Второе — единый формат ответа: и успех, и ошибка приходят предсказуемой структурой, например { "data": ... } или { "error": ... }, чтобы фронтенду не приходилось угадывать форму. Эти договорённости не входят в формальное определение REST, но именно они отличают учебный API от того, который не стыдно отдать в эксплуатацию другой команде.