Что такое идемпотентность
Что проверяют
Для фронтендера вопрос не про формальности спецификации, а про форму оплаты, по которой пользователь кликнул трижды, и про автоматические повторы в клиенте данных. Проверяют, различаете ли вы запросы, которые можно молча повторить, и те, которые повторять нельзя, и умеете ли договариваться с бэкендом о ключе операции.
Как отвечать
Идемпотентная операция при повторе оставляет систему в том же состоянии, что и после первого вызова. GET и HEAD ничего не меняют вообще, PUT и DELETE меняют, но повтор не добавляет эффекта, POST по умолчанию не идемпотентен.
Дальше про фронтенд. Блокировка кнопки на время запроса это мера удобства, а не гарантия: остаются вторая вкладка, перезагрузка страницы с повторной отправкой и повтор на уровне сети. Настоящая защита живёт на сервере, а от клиента требуется присылать при повторе тот же самый ключ.
const keyRef = useRef("");
if (!keyRef.current) keyRef.current = crypto.randomUUID();
await fetch("/api/orders", {
method: "POST",
headers: { "Idempotency-Key": keyRef.current },
body: JSON.stringify(order),
});Полезно знать политику своей библиотеки данных: запросы на чтение она обычно повторяет сама, мутации нет. Включать автоповтор мутаций без ключа значит заказывать дубли.
Где спотыкаются
Считают, что атрибут disabled на кнопке закрывает вопрос. Он спасает от двойного клика и не спасает от повторной отправки формы после обновления страницы, от второй вкладки и от ретрая внутри клиента.
Генерируют ключ внутри функции отправки, поэтому каждая попытка уходит с новым ключом и создаёт новый заказ. Ключ привязан к намерению пользователя, а не к вызову.
Считают любой GET безопасным по определению и спокойно вешают на него повторы. Если под таким адресом сервер продлевает сессию, списывает квоту или отмечает просмотр, безопасным он быть перестаёт, и двойной вызов в режиме разработки покажет это первым.