Питання 4 · Розділ 6

В чому різниця між PUT та PATCH?

Обидва методи використовуються для оновлення ресурсу, але працюють по-різному.

Мовні версії: English Russian Ukrainian

Junior Level

Обидва методи використовуються для оновлення ресурсу, але працюють по-різному.

PUT — Повне оновлення

  • Замінює все представлення ресурса на те, що надіслав клієнт. Полів, яких немає в тілі запиту, мають бути видалені або скинуті до значень за замовчуванням.
  • Потрібно надіслати всі поля, навіть ті, що не змінилися
  • Якщо не надіслати якеся поле — воно буде видалене або встановлене в null
PUT /users/1
{
  "name": "Ivan",
  "age": 30,
  "email": "ivan@example.com"
}

PATCH — Часткове оновлення

  • Оновлює лише ті поля, які були надіслані
  • Економить трафік: для об’єкта в 200 полів зміна одного поля через PATCH економить ~95% тіла запиту.
PATCH /users/1
{
  "age": 31
}
// Решта полів залишаються без змін

Порівняння:

Критерій PUT PATCH
Обсяг даних Повний об’єкт Тільки змінені поля
Ідемпотентність Завжди ідемпотентний Залежить від реалізації
Аналогія в SQL UPDATE всіх колонок UPDATE конкретних колонок

Коли PUT кращий за PATCH

Використовуйте PUT для повних замін невеликих об’єктів — простіший у реалізації та однозначний. Використовуйте PATCH для великих об’єктів або коли кілька клієнтів можуть паралельно змінювати різні поля.


Middle Level

Семантика методів

PUT (Replace)

  • Ідемпотентність: Завжди ідемпотентний. Скільки б разів не надіслати один і той самий запит — результат однаковий
  • DB Analogy: UPDATE users SET name=?, age=?, email=? ... (оновлення всіх колонок)

PATCH (Partial Update)

  • Ідемпотентність: Не гарантується. Залежить від реалізації
  • DB Analogy: UPDATE users SET age = age + 1 (не ідемпотентно) або SET age = 30 (ідемпотентно)

Специфікації PATCH

  1. JSON Merge Patch (RFC 7386):
    • Клієнт надсилає JSON: {"age": 31}
    • Сервер просто мержить його з поточним об’єктом
    • Мінус: Складно видалити поле (потрібно домовитися, що null — це видалення)

    Ключова відмінність: Merge Patch каже «що змінити» (декларативно), а JSON Patch каже «як змінити» (послідовність операцій: add, remove, replace).

  2. JSON Patch (RFC 6902):
    • Клієнт надсилає масив інструкцій:
      [
      { "op": "replace", "path": "/age", "value": 31 },
      { "op": "remove", "path": "/middleName" }
      ]
      
    • Плюс: Атомарність і гнучкість. Можна додавати елементи в масиви, видаляти конкретні поля

Оптимістичне блокування (ETag)

  1. Клієнт отримує ресурс із заголовком ETag: "v1"
  2. Клієнт надсилає PUT/PATCH із заголовком If-Match: "v1"
  3. Якщо ETag став “v2” — сервер поверне 412 Precondition Failed

Діагностика

  • 422 Unprocessable Entity: Якщо PATCH логічно невиконувана (наприклад, видалення обов’язкового поля)
  • Hibernate/JPA: При PATCH використовуйте nullValuePropertyMappingStrategy = IGNORE в MapStruct, щоб випадково не затерти поля

Senior Level

Проблема “Lost Updates” та Optimistic Locking

Що якщо два користувачі одночасно прочитали ресурс (age=20) і вирішили його оновити?

  • PUT-проблема: Перший оновив до 21, другий — до 25. Зміни першого втрачено.
  • PATCH-рішення: Якщо PATCH робить age = age + 1, підсумок буде 22 (обидві зміни враховано).

Продуктивність і Highload

  • Bandwidth: PATCH економить трафік на великих об’єктах (профіль із сотнями полів)
  • Validation: PUT простіше валідувати (повна схема). PATCH вимагає складної логіки валідації “тільки надісланих полів”
  • Reflection у Java: Реалізація PATCH часто вимагає Jackson JsonNode для визначення, чи було поле null або відсутнім

Реалізація PATCH у Spring

Будьте обережні з merge() у Hibernate. Якщо створити новий об’єкт і викликати merge(), можна випадково затерти поля, що не прийшли в PATCH, перетворивши його на кривий PUT.

Edge Cases

  • Якщо PATCH містить {"op": "add", "path": "/tags", "value": "new"} — це не ідемпотентно (при повторі додасться ще один тег)
  • Для ідемпотентності PATCH використовуйте детерміновані операції (replace, remove)

🎯 Шпаргалка для співбесіди

Обов’язково знати:

  • PUT замінює ресурс цілком — поля, яких немає в запиті, видаляються або обнуляються
  • PATCH оновлює лише надіслані поля — економить трафік на великих об’єктах
  • PUT завжди ідемпотентний; PATCH ідемпотентний тільки при детермінованих операціях
  • JSON Merge Patch (RFC 7386) — декларативний підхід: «що змінити»
  • JSON Patch (RFC 6902) — імперативний підхід: масив операцій (add, remove, replace)
  • Optimistic Locking через ETag + If-Match запобігає конфлікту паралельних оновлень
  • PUT простіше валідувати (повна схема), PATCH вимагає складної валідації часткових даних

Часті уточнюючі запитання:

  • Що таке Lost Update Problem? — Два клієнти читають ресурс і оновлюють паралельно; зміни першого втрачаються
  • Як реалізувати PATCH у Hibernate? — Використовувати nullValuePropertyMappingStrategy = IGNORE в MapStruct
  • Що поверне сервер при 412 Precondition Failed? — ETag змінився з моменту читання, ресурс застарів
  • Коли PUT кращий за PATCH? — Для невеликих об’єктів і коли потрібна однозначна повна заміна

Червоні прапорці (НЕ говорити):

  • «PATCH завжди ідемпотентний» — add у колекцію не ідемпотентне
  • «PUT оновлює лише передані поля» — PUT замінює весь об’єкт цілком
  • «Різниці між PUT і PATCH немає» — PUT = повна заміна, PATCH = часткове оновлення
  • «Merge Patch і JSON Patch — одне й те саме» — Merge Patch декларативна, JSON Patch — послідовність операцій

Пов’язані теми:

  • [[Що таке ідемпотентність (idempotency)]]
  • [[Які HTTP методи ідемпотентні]]
  • [[Які основні HTTP методи використовуються в REST]]
  • [[Як правильно іменувати REST endpoints]]