← Назад к вопросам

В чем разница между HTTP методами PUT() и PATCH()?

2.0 Middle🔥 161 комментариев
#API и интеграции

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между HTTP методами PUT() и PATCH()

Оба метода используются для обновления ресурсов, но они работают по-разному. Это критически важное различие при проектировании REST API.

PUT (Replace / Full Update)

Определение: PUT заменяет весь ресурс на сервере на отправленные данные. Это полное обновление.

Семантика:

  • Отправляешь полное представление ресурса
  • Сервер заменяет весь ресурс на отправленные данные
  • Если какие-то поля отсутствуют в запросе, они либо удаляются, либо устанавливаются в default значения
  • Идемпотентный: несколько одинаковых PUT запросов дают тот же результат

Пример:

Путь 1: Исходное состояние заказа
GET /api/v1/orders/123
{
  "id": "123",
  "status": "pending",
  "customer_name": "Ivan",
  "amount": 1000,
  "address": "Moscow",
  "notes": "Leave at door"
}

Путь 2: Отправляем PUT с ПОЛНЫМ заказом
PUT /api/v1/orders/123
Content-Type: application/json

{
  "id": "123",
  "status": "confirmed",
  "customer_name": "Ivan",
  "amount": 1000,
  "address": "Moscow",
  "notes": "Leave at door"
}

Результат: Заказ ПОЛНОСТЬЮ обновлен со всеми полями

Важный момент: Если в PUT запросе отсутствует поле, оно может быть удалено или установлено в default:

Путь 3: Отправляем PUT БЕЗ notes
PUT /api/v1/orders/123
Content-Type: application/json

{
  "id": "123",
  "status": "confirmed",
  "customer_name": "Ivan",
  "amount": 1000,
  "address": "Moscow"
  // notes отсутствует!
}

Результат: Может быть
- notes удалено (notes: null)
- ИЛИ notes установлено в default значение
- Зависит от реализации API

PATCH (Partial Update)

Определение: PATCH обновляет только указанные поля ресурса, остальные остаются неизменными. Это частичное обновление.

Семантика:

  • Отправляешь только изменённые поля (дельта)
  • Сервер обновляет только эти поля
  • Остальные поля остаются как были
  • Может быть идемпотентным (зависит от операции)

Пример:

Исходное состояние:
{
  "id": "123",
  "status": "pending",
  "customer_name": "Ivan",
  "amount": 1000,
  "address": "Moscow",
  "notes": "Leave at door"
}

Отправляем PATCH с ОДНИМ изменением
PATCH /api/v1/orders/123
Content-Type: application/json

{
  "status": "confirmed"
}

Результат: ТОЛЬКО status обновлён
{
  "id": "123",
  "status": "confirmed",      // обновлено
  "customer_name": "Ivan",     // без изменений
  "amount": 1000,              // без изменений
  "address": "Moscow",         // без изменений
  "notes": "Leave at door"     // без изменений
}

Таблица сравнения

ПараметрPUTPATCH
Тип обновленияПолное (replace)Частичное (merge)
Что отправляешьВесь ресурсТолько изменённые поля
Неупомянутые поляУдаляются / defaultОстаются как были
ИдемпотентностьВсегда идемпотентныйМожет быть, может нет
Размер payloadБольшеМеньше
Сложность реализацииПрощеСложнее
Когда использоватьЗамена всего ресурсаНебольшие изменения

Практические примеры

Сценарий 1: Обновление профиля пользователя

Исходные данные:

{
  "user_id": "f47ac10b",
  "first_name": "Ivan",
  "last_name": "Petrov",
  "email": "ivan@example.com",
  "phone": "+7-999-123-45-67",
  "bio": "Software Engineer",
  "avatar_url": "https://example.com/avatar.jpg"
}

Если использовать PUT для изменения только имени:

ПЫ, ОЙ! Забыли avatar_url, bio, phone
PUT /api/v1/users/f47ac10b

{
  "user_id": "f47ac10b",
  "first_name": "Ivan NEW",
  "last_name": "Petrov",
  "email": "ivan@example.com",
  "phone": "+7-999-123-45-67",
  "bio": "Software Engineer",
  "avatar_url": "https://example.com/avatar.jpg"
}

Результат: Корректно (если отправили весь объект)
НО если забыли avatar_url:
  avatar_url может быть удалён! ❌

Если использовать PATCH (правильно):

PATCH /api/v1/users/f47ac10b

{
  "first_name": "Ivan NEW"
}

Результат:
{
  "user_id": "f47ac10b",
  "first_name": "Ivan NEW",    // обновлено
  "last_name": "Petrov",        // без изменений
  "email": "ivan@example.com",  // без изменений
  "phone": "+7-999-123-45-67",   // без изменений
  "bio": "Software Engineer",    // без изменений
  "avatar_url": "...",           // без изменений ✓
}

Сценарий 2: Обновление статуса заказа

Используем PUT (полное обновление):

Если мы хотим изменить status, priority и notes:

Полный заказ с изменениями
PUT /api/v1/orders/123

{
  "id": "123",
  "customer_id": "cust-456",
  "items": [{...}],
  "status": "shipped",      // изменено
  "priority": "high",       // изменено
  "notes": "Ready",         // изменено
  "created_at": "2025-03-28T10:00:00Z",
  "updated_at": "2025-03-28T15:30:00Z"
}

Рисковано: если забыть items, created_at - они могут быть удалены!

Используем PATCH (правильно):

Только изменения
PATCH /api/v1/orders/123

{
  "status": "shipped",
  "priority": "high",
  "notes": "Ready"
}

Результат: Только эти 3 поля изменены, остальное как было

Сценарий 3: Замена конфигурации (PUT уместен)

Когда PUT имеет смысл:

Если ты заменяешь всю конфигурацию системы (например, настройки приложения):

PUT /api/v1/config

{
  "app_name": "MyApp",
  "theme": "dark",
  "language": "ru",
  "timeout": 30,
  "debug_mode": false,
  "features": {
    "feature_a": true,
    "feature_b": false
  }
}

Всё содержимое конфига заменяется ✓
Это правильное использование PUT

Идемпотентность

PUT всегда идемпотентный:

1-й раз: PUT /orders/123 -> успех
2-й раз: PUT /orders/123 (точный же запрос) -> тот же результат
3-й раз: PUT /orders/123 -> тот же результат

Результат одинаковый независимо от количества вызовов ✓

PATCH может не быть идемпотентным:

1-й раз: PATCH /orders/123 {"amount": 1000} -> успех
2-й раз: PATCH /orders/123 {"amount": 1000} -> тот же результат

НО если это операция типа {"amount": +100} (прибавить 100)
1-й раз: amount = 1000 + 100 = 1100
2-й раз: amount = 1100 + 100 = 1200

Неидемпотентный! ✗

JSON Merge Patch (RFC 7386) vs JSON Patch (RFC 6902)

Есть два способа описать PATCH операции:

JSON Merge Patch (простой):

Content-Type: application/merge-patch+json

{
  "status": "shipped",
  "notes": null  // явно удалить поле
}

Ресурс MERGE'ится с отправленными данными

JSON Patch (более мощный):

Content-Type: application/json-patch+json

[
  { "op": "replace", "path": "/status", "value": "shipped" },
  { "op": "add", "path": "/tags/-", "value": "urgent" },
  { "op": "remove", "path": "/notes" },
  { "op": "copy", "path": "/notes_backup", "from": "/notes" }
]

Больше control, но сложнее

Best Practices при проектировании API

1. Используй PATCH для частичных обновлений (по умолчанию)

// Правильно ✓
PATCH /api/v1/users/:id
Content-Type: application/json

{
  "email": "newemail@example.com"
}

// Не требуешь отправлять все поля

2. Используй PUT когда заменяешь весь ресурс

// Правильно ✓
PUT /api/v1/documents/:id
Content-Type: application/json

{
  // весь документ целиком
  "title": "...",
  "content": "...",
  "author": "...",
  // если поле отсутствует, оно удаляется
}

3. Требуй полный объект в PUT запросе

// PUT: требуй весь объект
PUT /api/v1/users/123 -> 400 Bad Request если отсутствуют поля

// PATCH: принимай любые поля
PATCH /api/v1/users/123 -> 200 OK для partial update

4. Документируй в API спецификации

PUT /users/{id}:
  description: Replace entire user resource
  requestBody:
    required: true
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/User'
        example:
          id: 123
          name: "John"
          email: "john@example.com"
          # all fields required

PATCH /users/{id}:
  description: Partial update of user
  requestBody:
    required: false
    content:
      application/json:
        schema:
          type: object
          properties:
            name:
              type: string
            email:
              type: string
          # no required fields

Мой подход в проектировании API

В проекте логистики я использовал:

  1. PATCH как основной метод для большинства обновлений

    • Меньше payload
    • Безопаснее (не забудешь поле)
    • Более гибкий
  2. PUT только для специальных случаев:

    • Замена конфигурации целиком
    • Замена документа/контракта целиком
    • Операции, которые требуют полного overwrite
  3. Всегда документирую разницу в API spec

  4. Использую JSON Merge Patch для простоты (он по умолчанию поддерживается браузерами)

Это дало нам более надёжный и user-friendly API.