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

Как сделать POST идемпотентным?

1.3 Junior🔥 171 комментариев
#API и интеграции

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

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

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

Как сделать POST идемпотентным

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

Проблема

Первый запрос: POST /api/v1/orders Результат: Заказ #001 создан

Если клиент не получил ответ и переотправил запрос: Второй запрос: POST /api/v1/orders Результат: Заказ #002 создан (дублевание!)

Это особенно важно, когда есть сетевые проблемы или таймауты.

Способ 1: Idempotency Key (Рекомендуемый)

Идея: клиент генерирует уникальный ключ для каждой операции. Сервер использует этот ключ, чтобы обнаружить повторные запросы.

Реализация на клиенте: Клиент генерирует UUID и отправляет его в заголовке Idempotency-Key. Сервер проверяет, был ли уже выполнен запрос с таким ключом.

Реализация на сервере:

Если idempotency_key в базе:
  → вернуть сохраненный результат
Иначе:
  → выполнить операцию
  → сохранить результат с ключом
  → вернуть результат

Схема БД:

idempotency_keys table:
- id (UUID primary key)
- idempotency_key (VARCHAR unique)
- request_body (JSONB)
- response_body (JSONB)
- status_code (INT)
- created_at (TIMESTAMP)
- expires_at (TIMESTAMP, 24 часа)

Способ 2: Уникальные ограничения (Unique Constraints)

Для операций, где можно однозначно определить дублирование по данным.

Пример: создание счета с уникальным номером

Если номер счета уже существует в БД, попытка вставить снова вызовет IntegrityError. Ловим исключение и возвращаем существующий ресурс.

Ограничение в БД:

ALTER TABLE invoices
ADD CONSTRAINT unique_invoice_number UNIQUE(invoice_number);

Способ 3: Проверка состояния (State-based Idempotency)

Перед созданием ресурса проверяем, не был ли он уже создан с такими же параметрами.

Логика:

  • Проверяем, есть ли пользователь с таким email
  • Если существует и данные совпадают → возвращаем его
  • Если существует, но данные не совпадают → ошибка 409 Conflict
  • Если не существует → создаем нового пользователя

Этот подход работает для операций, где ключ однозначно определяется данными.

Способ 4: Временные метки и версионирование

Для операций обновления можно использовать версионирование.

Идея:

  • Каждый ресурс имеет номер версии
  • Клиент отправляет текущую версию
  • Если версия не совпадает → конфликт (409)
  • После успешного обновления версия увеличивается

Это также защищает от race conditions.

Способ 5: Использование PUT вместо POST

По REST стандартам: PUT уже считается идемпотентным по определению.

Используй PUT для создания/обновления, когда у тебя есть ID ресурса:

PUT /api/v1/orders/{order_id}

Каждый вызов с одинаковыми данными дает одинаковый результат.

Best Practices

Время жизни ключа: 24 часа — достаточно для переотправки, но не вечно

Чистка старых ключей: Удаляй записи старше 24 часов из idempotency_keys таблицы

Логирование: логируй все повторные запросы для отладки и мониторинга

HTTP статус коды:

  • 200 OK — первый запрос выполнен
  • 200 OK — повторный запрос, результат из кеша
  • 409 Conflict — конфликт данных
  • 422 Unprocessable Entity — ошибка валидации

Где это критично:

  • Платежи и финансовые операции
  • Создание заказов
  • Регистрация пользователей
  • Любые операции, которые нельзя выполнить дважды
  • Трансферы денег

Какой способ выбрать?

Idempotency Key: лучший вариант для critical операций (платежи, заказы)

Unique Constraints: для простых случаев с естественным ключом

State-based: когда данные сами определяют уникальность

PUT вместо POST: по возможности используй PUT для идемпотентности

Вывод

Idempotency Key — это industrial standard, используемый Stripe, PayPal и другими платежными сервисами. Это лучший способ сделать POST идемпотентным, так как он явный, легко отлаживается и работает в любых сценариях.

Всегда думай об идемпотентности при проектировании API, особенно для операций, которые изменяют состояние.

Как сделать POST идемпотентным? | PrepBro