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

Чи є POST ідемпотентним?

Згідно зі специфікацією HTTP, метод POST офіційно неідемпотентний. Це означає, що повторний виклик POST з тими самими даними може призвести до іншого результату.

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

Junior Level

Ні, POST не є ідемпотентним.

Згідно зі специфікацією HTTP, метод POST офіційно неідемпотентний. Це означає, що повторний виклик POST з тими самими даними може призвести до іншого результату.

POST = дія. Кожна дія — нова. Два натискання “Купити” = дві покупки. У серйозних системах це вирішують спеціальним ключем ідемпотентності — про нього нижче.

Чому POST не ідемпотентний?

POST зазвичай використовується для:

  1. Створення ресурсів: POST /orders створить нове замовлення. Два виклики = два замовлення
  2. Обробки даних: POST /emails/send надішле два листи
  3. Додавання даних: Кожен виклик додає щось нове
POST /orders       → Створено замовлення #1
POST /orders       → Створено замовлення #2  (інший результат!)
POST /orders       → Створено замовлення #3  (ще один результат!)

Що це означає на практиці?

  • POST не можна автоматично повторювати при мережевих помилках
  • Кожен POST може створити новий побічний ефект
  • Браузери попереджають при повторній відправці POST-форми

Middle Level

RFC 9110 і неідемпотентність POST

POST призначений для обробки даних відповідно до логіки ресурсу. Це фундаментальна особливість, яка визначає поведінку браузерів, проксі-серверів і програмістів.

Патерн PRG (Post/Redirect/Get)

Класичне рішення проблеми неідемпотентності POST у веб-інтерфейсах:

  1. Користувач натискає “Оплатити” (POST)
  2. Сервер обробляє платіж і надсилає код 303 See Other з URL сторінки успіху
  3. Браузер робить GET на сторінку успіху

Результат: При натисканні F5 повторюється безпечний GET, а не небезпечний POST зі списанням грошей.

POST для пошуку

Коли GET-запит занадто довгий (багато фільтрів), використовують POST /search. Технічно він ідемпотентний (тільки читання), але семантично це POST.

Реалізація ідемпотентності для POST

В Enterprise-системах (Stripe, Adyen, AWS) POST роблять ідемпотентним за допомогою Idempotency-Key:

  • Клієнт генерує UUID і передає його в заголовку
  • Сервер використовує Distributed Lock (Redis) за ключем user_id + idempotency_key
  • Якщо запит вже в процесі — 409 Conflict
  • Якщо вже завершений — повертаємо збережений результат

Senior Level

Exactly-once delivery

У системах з чергами (Kafka, RabbitMQ) POST часто є точкою входу. Без ідемпотентності неможливо гарантувати “рівно одну” обробку повідомлення при повторних відправках (retries) продюсером.

Database Unique Constraints

Найдешевший спосіб забезпечити ідемпотентність POST — унікальний індекс за бізнес-ключами (наприклад, order_number). Однак це переносить логіку на рівень БД.

Патерн Idempotency-Key (деталі)

  • Використовується Distributed Lock (напр. Redis) за ключем user_id + idempotency_key
  • Якщо запит з таким ключем вже в процесі — 409 Conflict
  • Якщо вже завершений — повертаємо збережений результат (201 Created перетворюється на 200 OK при повторі)

Edge Cases

Коли неідемпотентність POST — це фіча

Коли користувач створює новий коментар, кожен POST має створити новий запис. Неідемпотентність тут — бажана поведінка, а не проблема.

POST для пошуку

Для таких випадків запропоновано метод QUERY (чернетка, поки не є стандартом). На практиці використовують POST /search.

Partial Failure

Якщо POST має створити 3 сутності, і створив лише 2? При повторі з тим самим ключем сервер має вміти “достворити” недостаюче або повернути помилку.

Діагностика і Моніторинг

  • Double-Submit Detection: Моніторинг запитів з однаковим тілом від одного користувача протягом короткого вікна (500мс) допомагає виявити баги UI
  • Header: X-Retry-Count: Корисно додавати на клієнті, щоб сервер розумів, чи є POST першою спробою або результатом автоматичного ретраю
  • Відстежуйте кількість повторних POST-запитів — це індикатор проблем із мережею або багів на клієнті

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

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

  • POST офіційно неідемпотентний за специфікацією HTTP (RFC 9110)
  • Кожен POST може створити новий ресурс або побічний ефект (5 POST /orders = 5 замовлень)
  • POST не можна автоматично повторювати при мережевому тайм-ауті — ризик подвійних оплат
  • Патерн PRG (Post/Redirect/Get) вирішує проблему повторної відправки: POST → 303 → GET
  • Idempotency-Key (UUID у заголовку) + Redis-lock роблять POST ідемпотентним
  • Unique Constraints на рівні БД — найдешевший спосіб забезпечити ідемпотентність POST
  • Неідемпотентність POST — це фіча, коли створюються нові коментарі, замовлення, платежі

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

  • Як Stripe робить POST ідемпотентним? — Клієнт передає Idempotency-Key, сервер зберігає результат у Redis
  • Що таке Exactly-Once Delivery? — Гарантія що повідомлення обробиться рівно один раз, попри повтори
  • Навіщо POST /search якщо є GET? — Коли GET-URL занадто довгий для безлічі фільтрів
  • Що робити при Partial Failure POST? — Сервер має вміти «достворити» недостаюче при повторі з тим самим ключем

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

  • «POST можна безпечно повторювати» — це ризик подвійних операцій
  • «POST ніколи не буває ідемпотентним» — можна через Idempotency-Key
  • «POST /search — це порушення REST» — допустимо коли GET-URL перевищує ліміт
  • «POST і PUT — одне й те саме для оновлення» — PUT замінює цілком, POST створює/обробляє

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

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