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

Для чего нужен PATCH?

2.0 Middle🔥 151 комментариев
#REST API и микросервисы

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

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

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

# Для чего нужен PATCH?

PATCH — это HTTP метод для частичного обновления ресурса. Отличие от PUT в том, что PATCH меняет только те поля, которые указаны в запросе, тогда как PUT заменяет весь ресурс.

PATCH vs PUT — ключевая разница

Сценарий

Есть пользователь с этими данными:

{
  "id": 1,
  "name": "John Doe",
  "email": "john@mail.com",
  "age": 30,
  "city": "New York"
}

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

PUT /api/v1/users/1
Content-Type: application/json

{
  "name": "Jane Doe",
  "email": "jane@mail.com"
}

Результат: Ресурс ПОЛНОСТЬЮ заменяется. Поля age и city теряются!

{
  "id": 1,
  "name": "Jane Doe",
  "email": "jane@mail.com",
  "age": null,
  "city": null
}

Использование PATCH (частичное обновление)

PATCH /api/v1/users/1
Content-Type: application/json

{
  "name": "Jane Doe",
  "email": "jane@mail.com"
}

Результат: Обновляются ТОЛЬКО указанные поля. Остальное остаётся неизменным!

{
  "id": 1,
  "name": "Jane Doe",
  "email": "jane@mail.com",
  "age": 30,
  "city": "New York"
}

Сравнение HTTP методов

МетодЦельИдемпотентностьКогда использовать
GETПолучить ресурс✓ ДаЧтение данных
POSTСоздать ресурс✗ НетСоздание новых данных
PUTЗаменить весь ресурс✓ ДаПолное обновление
PATCHОбновить часть ресурса✓ ДаЧастичное обновление
DELETEУдалить ресурс✓ ДаУдаление данных

Примеры использования PATCH

1. Обновление профиля пользователя

# Обновляем только город
PATCH /api/v1/users/1
Content-Type: application/json

{
  "city": "Los Angeles"
}

2. Обновление статуса задачи

# Только меняем статус, остальное не трогаем
PATCH /api/v1/tasks/42
Content-Type: application/json

{
  "status": "completed"
}

3. Обновление прав доступа

# Добавляем новую роль
PATCH /api/v1/users/100/roles
Content-Type: application/json

{
  "add": ["admin"]
}

Реализация в Spring Boot

1. Controller с PATCH

import org.springframework.web.bind.annotation.*;
import org.springframework.http.HttpStatus;

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    
    private UserService userService;
    
    // PATCH для частичного обновления
    @PatchMapping("/{id}")
    public ResponseEntity<UserDTO> partialUpdate(
        @PathVariable Long id,
        @RequestBody UserPatchDTO patchDTO
    ) {
        UserDTO updated = userService.partialUpdate(id, patchDTO);
        return ResponseEntity.ok(updated);
    }
    
    // PUT для полного обновления (для сравнения)
    @PutMapping("/{id}")
    public ResponseEntity<UserDTO> fullUpdate(
        @PathVariable Long id,
        @RequestBody UserDTO userDTO
    ) {
        UserDTO updated = userService.fullUpdate(id, userDTO);
        return ResponseEntity.ok(updated);
    }
}

2. DTO для PATCH

// DTO для PATCH (все поля Optional)
public class UserPatchDTO {
    private Optional<String> name;
    private Optional<String> email;
    private Optional<Integer> age;
    private Optional<String> city;
    
    // Getters/setters
}

// DTO для PUT (все поля обязательны)
public class UserDTO {
    private String name;
    private String email;
    private Integer age;
    private String city;
    
    // Getters/setters
}

3. Service реализация

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    // Частичное обновление (PATCH)
    public UserDTO partialUpdate(Long id, UserPatchDTO patchDTO) {
        User user = userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException(id));
        
        // Обновляем ТОЛЬКО переданные поля
        patchDTO.getName().ifPresent(user::setName);
        patchDTO.getEmail().ifPresent(user::setEmail);
        patchDTO.getAge().ifPresent(user::setAge);
        patchDTO.getCity().ifPresent(user::setCity);
        
        userRepository.save(user);
        return toDTO(user);
    }
    
    // Полное обновление (PUT)
    public UserDTO fullUpdate(Long id, UserDTO userDTO) {
        User user = userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException(id));
        
        // Заменяем ВСЕ поля
        user.setName(userDTO.getName());
        user.setEmail(userDTO.getEmail());
        user.setAge(userDTO.getAge());
        user.setCity(userDTO.getCity());
        
        userRepository.save(user);
        return toDTO(user);
    }
}

JSON Merge Patch (RFC 7386)

Для PATCH часто используется стандарт JSON Merge Patch:

{
  "name": "Jane",
  "age": null  // null = удалить это поле
}

JSON Patch (RFC 6902) — более сложный способ

[
  { "op": "replace", "path": "/name", "value": "Jane" },
  { "op": "remove", "path": "/age" },
  { "op": "add", "path": "/phone", "value": "+1234567890" }
]

Когда использовать PATCH

Обновление отдельных полей — пользователь меняет только город

Большие объекты — когда отправка всех данных затратна

Частые небольшие обновления — статус, флаги, счётчики

API, где клиент может знать только часть данных — мобильное приложение

Исправление конкретных ошибок — клиент исправляет только ошибку

Когда использовать PUT

Замена всего ресурса — загрузка полного профиля

Когда нужна идемпотентность с гарантией — либо будут ВСЕ данные, либо старые

Создание с известным ID — PUT может создать ресурс, если его нет

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

Оба метода идемпотентны:

  • Несколько одинаковых PATCH запросов = результат как одного
  • Несколько одинаковых PUT запросов = результат как одного

Вывод

PATCH нужен для частичного обновления ресурсов в REST API. Главное отличие от PUT:

  • PATCH: обновляет только указанные поля
  • PUT: заменяет весь ресурс

Выбирай PATCH, когда нужно обновить несколько полей из большого объекта. Используй PUT, когда нужна полная замена.