В главе 3.1 был общий принцип: PUT — перезапись целиком, PATCH — частичное обновление. Сейчас разбираем, как это выглядит руками и где обычно ошибаются.
Стартовая точка
Пусть на сервере есть пост:
GET /api/posts/42
→ {
"id": 42,
"title": "Старый заголовок",
"body": "Старое тело",
"author": "anna",
"tags": ["css", "html"],
}
Мы хотим поменять только title. Сравним два подхода.
Через PUT — шлём ресурс целиком
// Сначала достаём текущий ресурс:
const current = await fetch('/api/posts/42').then(r => r.json());
// Меняем поле:
current.title = 'Новый заголовок';
// Шлём всё назад:
const response = await fetch('/api/posts/42', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(current),
});
На сервере пост заменится целиком тем, что мы прислали. Поля body, author, tags сохранятся — потому что мы их сами вложили в тело. Если бы мы их забыли — они исчезли бы (или приняли дефолтные значения).
Это и есть ловушка PUT: не прислал поле — считай, что обнулил. По строгой спецификации сервер должен заменить ресурс ровно на то, что прислали. На практике многие API мягче — не трогают незаявленные поля, — но полагаться на это нельзя.
Через PATCH — шлём только дельту
const response = await fetch('/api/posts/42', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Новый заголовок' }),
});
Короче, понятнее, эффективнее. Никаких лишних полей в теле, сервер сам разберётся, что обновить, а остальное оставить.
Это самый частый паттерн «поменять один чекбокс»: дёргаем PATCH с тем единственным полем, которое менялось. Никаких лишних round-trip-ов для подгрузки текущего состояния.
Когда выбрать какой
- PUT — когда у вас на руках уже есть весь ресурс и логично его перезаписать. Например, форма «редактировать профиль» с двадцатью полями: пользователь много чего поменял, и логичнее отправить весь новый профиль одним сообщением.
- PATCH — когда меняется одно-два поля. Переключатель «активно», обновление аватарки, переименование.
Если документация API явно говорит, какой метод использовать — следуйте ей, даже если кажется «неправильно». REST — конвенция, а не закон, и каждый сервис её интерпретирует немного по-своему.
JSON Patch — для перфекционистов
Стандарт RFC 6902 описывает специальный формат для PATCH-запросов — JSON Patch. Тело — массив операций:
PATCH /api/posts/42
Content-Type: application/json-patch+json
[
{ "op": "replace", "path": "/title", "value": "Новый заголовок" },
{ "op": "add", "path": "/tags/-", "value": "javascript" },
{ "op": "remove", "path": "/author" }
]
Операции: add, remove, replace, copy, move, test. Позволяет описать сложные точечные изменения, в том числе во вложенных полях. Поддерживается далеко не каждым API — смотрите документацию. Если у API нет JSON Patch, шлите обычный JSON-объект с полями.
Что часто делает не так начинающий
- На PUT шлёт только изменённые поля — и у пользователя «исчезает» имя или аватарка.
- На PATCH шлёт весь объект (включая id, created_at и прочую системщину) — сервер ругается на попытку поменять readonly-поля.
- Использует POST вместо PATCH «потому что POST умеет всё». Технически — работает; стилистически — не REST.
Дальше — единственная в этом модуле глава про входящие заголовки: кэширование.