Idempotencia
🎯 Objetivo
Regla
14.1: Se debe comprender y aplicar el concepto de idempotencia correctamente para los métodos HTTP que lo requieren (GET, PUT, DELETE, HEAD, OPTIONS).
14.2: Se debe proporcionar un mecanismo para que los clientes puedan realizar solicitudes POST o PATCH de forma idempotente cuando sea necesario para evitar efectos secundarios no deseados debido a reintentos de red.
📖 Concepto y Definición
Regla
14.3: Una operación (solicitud HTTP) es idempotente si realizarla múltiples veces tiene el mismo efecto en el estado del servidor que realizarla una sola vez. El resultado (respuesta) de la segunda y subsiguientes solicitudes puede diferir (ej. un DELETE puede dar 204 la primera vez y 404 las siguientes), pero el estado final del servidor debe ser el mismo.
14.4: Métodos HTTP Idempotentes por Naturaleza (Norma 3.3 - Propiedades):
GET,HEAD,OPTIONS: Son seguros y, por lo tanto, inherentemente idempotentes. Su implementación debe mantener esta propiedad.PUT: Debe ser implementado como idempotente (reemplazo total del recurso).DELETE: Debe ser implementado como idempotente (eliminar el recurso). 14.5: Métodos HTTP No Idempotentes por Naturaleza:POST: Generalmente no es idempotente (ej. crea un nuevo recurso cada vez). Puede ser problemático si un cliente reintenta una solicitudPOSTdebido a un fallo de red temporal (podría crear duplicados).PATCH: No es inherentemente idempotente (ej.PATCH /counter { "op": "increment", "value": 1 }). Reintentarlo podría incrementar el contador múltiples veces.
Mecanismo Obligatorio para Idempotencia en POST/PATCH:
Regla
14.6: Para operaciones POST o PATCH donde la creación accidental de duplicados o la aplicación múltiple de efectos secundarios debido a reintentos sea un riesgo significativo (ej. crear un pago, procesar una orden), la API debe soportar un mecanismo de idempotencia basado en una clave proporcionada por el cliente.
14.7: El mecanismo obligatorio es el uso del encabezado Idempotency-Key en la solicitud:
- El cliente debe generar una clave única (preferiblemente un UUID v4) para cada operación que desea realizar de forma idempotente.
- El cliente debe enviar esta clave en el encabezado
Idempotency-Key: <valor-uuid-unico>. - El cliente debe reintentar la misma solicitud (misma URI, método, cuerpo y
Idempotency-Key) si no recibe una respuesta definitiva (ej. timeout de red). 14.8: El servidor debe: - Al recibir una solicitud
POSToPATCHconIdempotency-Key, buscar si ya ha procesado (o está procesando) una solicitud con esa misma clave. - Si la clave es nueva: procesar la solicitud normalmente, almacenar el resultado (código de estado y cuerpo de la respuesta) asociado a esa clave idempotente durante un período razonable (ej. 24 horas), y devolver el resultado.
- Si la clave ya existe y la operación original se completó: no volver a ejecutar la operación. En su lugar, debe devolver inmediatamente el resultado almacenado previamente (mismo código de estado y cuerpo).
- Si la clave ya existe pero la operación original aún está en progreso: puede devolver un error de conflicto (ej.
409 Conflict) para indicar al cliente que espere y reintente más tarde, o esperar a que la operación original termine.
🤔 ¿Por qué es Importante? / Beneficios Clave (Contexto)
- Fiabilidad: Evita la creación de recursos duplicados o la aplicación múltiple de acciones debido a problemas de red y reintentos del cliente.
- Robustez: Hace que las interacciones con la API sean más resistentes a fallos transitorios.
- Experiencia del Cliente: Previene resultados inesperados para el usuario final (ej. cobros duplicados).
✅ Buenas Prácticas y Recomendaciones Clave
Regla
14.9: Se refuerza la Norma 14.4: La implementación de PUT y DELETE debe ser estrictamente idempotente.
14.10: Se refuerza la Norma 14.6 y 14.7: El soporte del encabezado Idempotency-Key es obligatorio para operaciones POST y PATCH sensibles a duplicados o efectos secundarios por reintentos.
14.11: La unicidad de la Idempotency-Key debe ser gestionada por el cliente. Se recomienda el uso de UUIDs v4.
14.12: El servidor debe almacenar el resultado de la solicitud original (código de estado y cuerpo) asociado a la Idempotency-Key durante un tiempo suficiente para cubrir posibles reintentos, pero no indefinidamente (ej. 24 horas).
14.13: La implementación del almacenamiento y recuperación de claves/resultados idempotentes debe ser eficiente.
14.14: La documentación de la API (Norma 9.5) debe indicar claramente qué endpoints POST/PATCH soportan Idempotency-Key y cómo usarlo.
Don'ts (Prohibido):
- 14.15: No se debe implementar
PUToDELETEde forma no idempotente. - 14.16: No se debe ignorar el encabezado
Idempotency-Keysi se recibe en un endpoint que lo soporta. - 14.17: No se debe reutilizar una
Idempotency-Keypara operaciones lógicamente diferentes. - 14.18: No se debe asumir que todas las operaciones
POSToPATCHnecesitanIdempotency-Key. Debe aplicarse donde el riesgo de reintento sea problemático.
💡 Ejemplos Prácticos (Ilustrativos)
Ejemplo 1: Cliente Realizando Pago con Idempotencia
Primera Solicitud:
POST /v1/charges HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: f1d2d2f9-1a2b-4c3d-8e4f-5a6b7c8d9e0f // UUID v4 generado por cliente
{
"amount": 1000,
"currency": "usd",
"source": "tok_visa"
}
Respuesta del Servidor (Éxito):
HTTP/1.1 201 Created
Content-Type: application/json
Request-Id: req_123 // ID único de esta solicitud
{
"chargeId": "ch_abc123",
"status": "succeeded",
"amount": 1000
}
El servidor almacena esta respuesta asociada a la clave f1d2d2f9-1a2b-4c3d-8e4f-5a6b7c8d9e0f.
Ejemplo 2: Cliente Reintentando Debido a Timeout
El cliente no recibió la respuesta anterior (timeout). Reintenta exactamente la misma solicitud:
POST /v1/charges HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: f1d2d2f9-1a2b-4c3d-8e4f-5a6b7c8d9e0f // MISMA CLAVE
{
"amount": 1000,
"currency": "usd",
"source": "tok_visa"
}
Respuesta del Servidor (Resultado Idempotente):
El servidor detecta la Idempotency-Key duplicada, no procesa el pago de nuevo, y devuelve la respuesta almacenada:
HTTP/1.1 201 Created // Mismo código de estado original
Content-Type: application/json
Request-Id: req_456 // Nuevo ID para esta solicitud, pero resultado original
Idempotency-Replayed: true // Encabezado opcional indicando reintento
{
"chargeId": "ch_abc123", // Mismo resultado original
"status": "succeeded",
"amount": 1000
}
Resultado: Solo se realizó un cobro.
🛠️ Herramientas y Consideraciones Adicionales (Contexto)
- Generación de Claves: Los clientes deben usar generadores de UUID robustos.
- Almacenamiento de Claves: El servidor necesita un almacenamiento eficiente (ej. Redis, base de datos) para las claves y resultados, con políticas de expiración.
- Alcance de la Clave: La clave idempotente debe ser única por cliente/usuario autenticado para evitar colisiones.
- Casos de Uso: Es especialmente crítico para APIs financieras, procesamiento de pedidos, o cualquier operación donde la ejecución múltiple sea perjudicial.