Logo
Overview

HTTP-методы REST API: GET, POST, PUT, PATCH, DELETE — когда какой применять

August 12, 2026
10 min read

Вы наверняка видели эти пять слов в спецификациях и коде. GET, POST, PUT, PATCH, DELETE. Кто-то использует первые два и закрывает все потребности. Кто-то путает PUT и PATCH. А кто-то пихает JSON в GET-запрос и удивляется, что балансировщик режет тело запроса.

Метод в HTTP — не просто техническая деталь. Это семантический контракт между клиентом и сервером. Выбор метода говорит серверу: «я хочу прочитать», «я хочу создать», «я хочу заменить целиком» или «поправь вот это поле». Сервер, в свою очередь, обещает вести себя предсказуемо: отдавать одни и те же данные на повторный GET, не плодить дубликаты при повторном PUT и так далее.

Если вы ещё не разобрались с базовыми принципами REST — начните с основных принципов REST API. А текущий пост — про методы: их семантика, свойства, ошибки и дерево решений.

Что такое HTTP-методы и зачем они нужны

HTTP-метод — это глагол, который задаёт намерение запроса. Представьте официанта в ресторане. Вы можете попросить меню (GET), заказать новое блюдо (POST), попросить заменить остывший стейк целиком (PUT), попросить убрать лук из салата (PATCH) или отменить заказ (DELETE). Глагол определяет, что произойдёт с ресурсом на сервере.

Формально методы описаны в RFC 7231 (и дополнены в RFC 5789 для PATCH). Но на практике достаточно знать пять: GET, POST, PUT, PATCH и DELETE. Остальные (HEAD, OPTIONS, TRACE, CONNECT) используются реже и решающим образом на архитектуру не влияют.

Важно: метод ничего не гарантирует сам по себе. Сервер может реализовать GET, удаляющий запись из базы, — и HTTP-спецификация не запрещает. Но это нарушение семантики, и ваш API быстро превратится в минное поле. Семантика методов — это конвенция, которую все ожидают, и ломать её без крайней нужды не стоит (а с крайней нуждой стоит пересмотреть архитектуру).

Пять методов: полный разбор

GET — читаем, не вредим

GET — самый частый метод в REST API. Его задача: получить представление ресурса и ничего не изменить.

Ключевые свойства:

  • Безопасный (safe) — не меняет состояние сервера. Повторный GET на тот же URL должен возвращать те же данные (с поправкой на время).
  • Идемпотентный (idempotent) — сколько бы раз вы ни повторили запрос, результат один и тот же. Повторный GET не создаст дубликатов, не изменит счётчиков, не спишет деньги.
  • Кэшируемый — браузеры, CDN и прокси могут кэшировать ответы на GET. Это один из столпов производительности REST.

Пример:

GET /api/orders/42 HTTP/1.1
Authorization: Bearer eyJhbGciOi...

Что возвращает: заказ №42 в формате JSON. Никаких побочных эффектов.

GET /api/orders?status=active&page=1&limit=20 HTTP/1.1

Что возвращает: первую страницу активных заказов. Фильтрация и пагинация передаются в query-параметрах.

Ошибка, которую допускают: передавать тело в GET-запросе. HTTP-спецификация этого не запрещает, но семантика GET говорит: «я читаю, а не отправляю данные». Балансировщики, кэширующие прокси и некоторые библиотеки тело GET-запроса просто игнорируют или отбрасывают. Результат: ваш фильтр { "status": "active" } в теле запроса не долетает до сервера, а вы гадаете, почему API возвращает все заказы подряд.

POST — создаём новое

POST — второй по частоте метод. Его задача: создать новый ресурс или выполнить операцию, не сводимую к CRUD.

Ключевые свойства:

  • Небезопасный — меняет состояние сервера. Повторный POST создаст ещё один ресурс, заспамит базу дубликатами или, того хуже, инициирует повторный платёж.
  • Неидемпотентный — каждый вызов даёт разный результат. Про это свойство часто забывают и потом разгребают последствия.

Пример создания заказа:

POST /api/orders HTTP/1.1
Content-Type: application/json
{
"customer_id": 42,
"items": [
{"product_id": 101, "quantity": 2},
{"product_id": 205, "quantity": 1}
]
}

Что возвращает: 201 Created с заголовком Location: /api/orders/789 и телом созданного заказа.

POST — единственный метод, который не обязан быть идемпотентным. Если вам нужно создать ресурс и гарантировать отсутствие дублей при повторной отправке (например, платёж), — это отдельная задача, решаемая через ключ идемпотентности (но об этом в другом посте).

Отдельный сценарий для POST — операции, которые не вписываются в CRUD:

POST /api/orders/789/send-to-fulfillment HTTP/1.1

Здесь мы не создаём ресурс, а запускаем бизнес-операцию: «отправить заказ на сборку». REST в чистом виде предлагает моделировать такие операции как создание подресурса (например, POST /api/orders/789/fulfillments). На практике многие команды используют POST для RPC-подобных вызовов — и это работает, пока вы осознаёте, что отходите от REST-конвенций.

PUT — перезаписываем целиком

PUT — метод для полной замены ресурса. Отправляете полное представление объекта, и сервер записывает его вместо старого.

Ключевые свойства:

  • Небезопасный — меняет состояние.
  • Идемпотентный — повторный PUT с тем же телом даст тот же результат. Это важно: PUT гарантирует, что сколько бы раз ни пришёл один и тот же запрос, итоговое состояние ресурса не изменится.

Пример: заказчик меняет адрес доставки. Вы отправляете полный заказ с новым адресом:

PUT /api/orders/789 HTTP/1.1
Content-Type: application/json
{
"id": 789,
"customer_id": 42,
"items": [...],
"address": "г. Москва, ул. Новая, д. 10"
}

Что возвращает: 200 OK с обновлённым заказом. Если ресурса не существовало — некоторые API создают его и возвращают 201 Created. Но это уже спорная практика: строгий REST предполагает, что клиент знает URL создаваемого ресурса заранее.

Важный нюанс: PUT требует полного представления. Если вы отправите заказ без поля items, сервер имеет право (и должен, по спецификации) затереть товары в заказе пустым массивом. Именно из-за этого PUT часто заменяют на PATCH — когда нужно обновить одно-два поля, а не гонять весь объект туда-сюда.

PATCH — обновляем частично

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

Ключевые свойства:

  • Небезопасный — меняет состояние.
  • Неидемпотентный в общем случае. Важно: PATCH может быть идемпотентным, но не обязан. Если вы отправляете {"status": "delivered"} — повторный вызов ничего не сломает (статус уже delivered). Но если вы отправляете {"$inc": {"counter": 1}} — каждый вызов увеличит счётчик. Всё зависит от формата PATCH-документа.

Два популярных формата PATCH:

JSON Merge Patch (RFC 7396) — простой: отправляете JSON с полями, которые хотите обновить. Не позволяет удалить поле (можно только установить в null).

PATCH /api/orders/789 HTTP/1.1
Content-Type: application/merge-patch+json
{
"address": "г. Москва, ул. Новая, д. 15",
"status": "confirmed"
}

JSON Patch (RFC 6902) — операции над документом: добавить, заменить, удалить, переместить.

PATCH /api/orders/789 HTTP/1.1
Content-Type: application/json-patch+json
[
{ "op": "replace", "path": "/address", "value": "г. Москва, ул. Новая, д. 15" },
{ "op": "remove", "path": "/discount_code" }
]

JSON Patch мощнее, но сложнее для клиента. Merge Patch интуитивен, но не позволяет удалить поле (только обнулить). Выбор — всегда компромисс между гибкостью API и простотой интеграции.

На практике многие команды используют PATCH с Merge Patch для 95% сценариев. JSON Patch оправдан, когда у вас сложные документы и нужна атомарность операций над разными полями.

DELETE — удаляем

DELETE — метод для удаления ресурса.

Ключевые свойства:

  • Небезопасный — меняет состояние (очевидно).
  • Идемпотентный — повторный DELETE на тот же URL не должен падать с ошибкой. Первый вызов удаляет, второй — возвращает 404 Not Found или 204 No Content, и оба варианта корректны. Главное, чтобы сервер не упал с исключением «ресурс уже удалён».

Пример:

DELETE /api/orders/789 HTTP/1.1

Что возвращает: 204 No Content (успех, но тела нет) или 200 OK с подтверждением удаления. Второй вариант полезен, когда клиенту нужно показать удалённые данные (например, в undo-операции).

Нюанс: удалять ли связанные ресурсы — архитектурное решение. Удаление заказа может каскадно удалить позиции заказа, а может оставить их осиротевшими. Первый подход консистентен, второй позволяет восстановить заказ. Выбирайте по бизнес-требованиям.

Безопасность и идемпотентность

Два свойства, которые чаще всего путают. Давайте разложим.

  • Безопасный метод — не меняет состояние сервера. Только GET и HEAD (и OPTIONS).
  • Идемпотентный метод — повторный вызов с теми же параметрами даёт тот же результат. GET, PUT, DELETE — идемпотентны. POST — нет. PATCH — зависит от документа.

Безопасный метод всегда идемпотентен, но не наоборот. PUT идемпотентен (повторная замена даёт то же состояние), но не безопасен (состояние меняется).

100%
graph TD
  A["HTTP-запрос клиента"] --> B{"Метод запроса?"}
  B -->|"GET"| C["GET: Чтение данных"]
  B -->|"POST"| D["POST: Создание ресурса"]
  B -->|"PUT"| E["PUT: Полная замена"]
  B -->|"PATCH"| F["PATCH: Частичное обновление"]
  B -->|"DELETE"| G["DELETE: Удаление"]
  C --> C1["Безопасный, Идемпотентный, Кэшируемый
Код: 200 OK"]
  D --> D1["Небезопасный, Неидемпотентный
Код: 201 Created"]
  E --> E1["Небезопасный, Идемпотентный
Код: 200 / 204"]
  F --> F1["Небезопасный, Условно-идемпотентный
Код: 200 OK"]
  G --> G1["Небезопасный, Идемпотентный
Код: 204 No Content"]
  style A fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style B fill:#f0a500,stroke:#c88400,color:#fff
  style C fill:#50c878,stroke:#3a9a5c,color:#fff
  style D fill:#7b68ee,stroke:#5a4db2,color:#fff
  style E fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style F fill:#f0a500,stroke:#c88400,color:#fff
  style G fill:#e0e0e0,stroke:#999,color:#333
  style C1 fill:#50c878,stroke:#3a9a5c,color:#fff
  style D1 fill:#7b68ee,stroke:#5a4db2,color:#fff
  style E1 fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style F1 fill:#f0a500,stroke:#c88400,color:#fff
  style G1 fill:#e0e0e0,stroke:#999,color:#333

На диаграмме видно чёткое разделение: GET (зелёный) — единственный полностью безопасный метод. POST (фиолетовый) — единственный гарантированно неидемпотентный. PUT и DELETE — небезопасные, но идемпотентные. PATCH — дитя компромисса: безопасным не назовёшь, с идемпотентностью тоже не всё однозначно.

Зачем вообще заморачиваться идемпотентностью? Представьте платёжный шлюз. Клиент отправил POST на списание средств, интернет моргнул, ответ не дошёл. Клиент повторяет запрос. Если метод неидемпотентен — спишется дважды. Если идемпотентен (PUT с полным состоянием или POST с ключом идемпотентности) — повторный запрос безопасен. В мире распределённых систем, где сеть ненадёжна по определению, идемпотентность — не роскошь, а средство выживания.

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

МетодНазначениеБезопасныйИдемпотентныйКэшируемыйТипичный код ответа
GETЧтение ресурсаДаДаДа200 OK
POSTСоздание, RPC-операцииНетНетНет201 Created
PUTПолная заменаНетДаНет200 / 204
PATCHЧастичное обновлениеНетЗависитНет200 OK
DELETEУдалениеНетДаНет204 No Content

Если хотите детально разобраться, какие коды ответов возвращать в каждом сценарии — загляните в справочник HTTP-кодов ответов. Там разобраны все 1xx–5xx с примерами из реальной практики.

Как выбрать метод: дерево решений

Никакой магии — просто логические вопросы:

  1. Вы читаете данные и не меняете состояние? → GET
  2. Создаёте новый ресурс, и сервер назначает ID? → POST
  3. Заменяете ресурс целиком, и клиент знает URL? → PUT
  4. Меняете одно-два поля, не гоняя весь объект? → PATCH
  5. Удаляете ресурс? → DELETE

Универсальное правило, которое выручает в спорных ситуациях: если вы сомневаетесь между POST и PUT — спросите себя, приведёт ли повторный запрос к дубликату. Если да — используйте PUT или добавьте ключ идемпотентности к POST.

Типичные ошибки

GET с телом запроса. Технически HTTP позволяет. На практике — балансировщики, кэши и прокси режут тело GET как нестандартное поведение, и ваши параметры фильтрации теряются где-то по дороге. Фильтры, сортировку и пагинацию передавайте в query-параметрах.

POST для всего. Команда договорилась: «Пишем всё через POST, чтобы не заморачиваться». Через полгода методология «POST для всего» аукается. Кэширование не работает (POST не кэшируется). Идемпотентности нет (повторные запросы плодят дубликаты). Отладка превращается в гадание: этот POST что-то создаёт или обновляет? Экономия пяти минут на выборе метода оборачивается часами дебага.

PUT для частичного обновления. Отправляете PUT /api/orders/789 с {"status": "delivered"} — и сервер затирает весь заказ, оставляя только поле статуса. То, что у вас в коде контроллер додумывает недостающие поля из базы — не заслуга PUT, а костыль, который сломается при смене разработчика. Используйте PATCH для частичных обновлений.

Игнорирование кодов ответа. 200 OK на создание ресурса вместо 201 Created — мелочь? Нет. Клиентские библиотеки, мониторинг и автоматические тесты полагаются на семантику кодов. Если ваш API на создание возвращает 200 — автоматическая проверка «создался ли ресурс» усложняется: приходится парсить тело ответа вместо проверки статус-кода.

Заключение

Пять методов — это не бюрократия и не «перегруз» для простого API. Это язык, на котором клиент и сервер договариваются о намерениях. GET значит «дай посмотреть». POST — «создай». PUT — «замени целиком». PATCH — «подправь деталь». DELETE — «удали». Просто. Понятно. Предсказуемо.

Стоит один раз договориться в команде о семантике методов — и REST API перестаёт быть источником сюрпризов. А сюрпризы в продакшене — это именно то, чего мы все пытаемся избежать, не так ли?

PS. Если вы дочитали до этого места и узнали свой API в разделе «Типичные ошибки» — не переживайте. Мы все через это проходили. Главное — не продолжать.