Manejo de Errores
Aprende cómo manejar errores de manera elegante en tu integración con la API de LosCenotes.
Formato de Respuesta de Error
Todos los errores de la API siguen una estructura consistente:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Uno o más campos son inválidos",
"status": 400,
"details": {
"field": "email",
"constraint": "Formato de email inválido"
},
"requestId": "req_1234567890abcdef",
"timestamp": "2024-01-15T10:30:00Z"
},
"_sandbox": true // Solo en modo sandbox
}
Códigos de Estado HTTP
La API de LosCenotes usa códigos de estado HTTP estándar:
| Código de Estado | Significado | Descripción |
|---|---|---|
| 200 | OK | Petición exitosa |
| 201 | Created | Recurso creado exitosamente |
| 400 | Bad Request | Parámetros de petición inválidos |
| 401 | Unauthorized | Clave API inválida o faltante |
| 403 | Forbidden | Permisos insuficientes |
| 404 | Not Found | Recurso no encontrado |
| 409 | Conflict | El recurso ya existe |
| 422 | Unprocessable Entity | Falló la validación |
| 429 | Too Many Requests | Límite de tasa excedido |
| 500 | Internal Server Error | Error del servidor |
| 503 | Service Unavailable | Servicio temporalmente no disponible |
Códigos de Error
Errores de Autenticación
INVALID_API_KEY
{
"success": false,
"error": {
"code": "INVALID_API_KEY",
"message": "La clave API proporcionada es inválida",
"status": 401
}
}
AUTHENTICATION_REQUIRED
{
"success": false,
"error": {
"code": "AUTHENTICATION_REQUIRED",
"message": "Se requiere autenticación para este endpoint",
"status": 401
}
}
INSUFFICIENT_PERMISSIONS
{
"success": false,
"error": {
"code": "INSUFFICIENT_PERMISSIONS",
"message": "Tu clave API no tiene permisos para esta acción",
"status": 403
}
}
Errores de Validación
VALIDATION_ERROR
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Uno o más campos son inválidos",
"status": 400,
"details": [
{
"field": "name",
"constraint": "El nombre debe tener entre 1 y 100 caracteres"
},
{
"field": "email",
"constraint": "Formato de email inválido"
}
]
}
}
MISSING_REQUIRED_FIELD
{
"success": false,
"error": {
"code": "MISSING_REQUIRED_FIELD",
"message": "Falta un campo requerido",
"status": 400,
"details": {
"field": "cenoteId"
}
}
}
Errores de Recursos
RESOURCE_NOT_FOUND
{
"success": false,
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "El recurso solicitado no fue encontrado",
"status": 404,
"details": {
"resource": "cenote",
"id": "c3n0t3-not-found"
}
}
}
RESOURCE_ALREADY_EXISTS
{
"success": false,
"error": {
"code": "RESOURCE_ALREADY_EXISTS",
"message": "Ya existe un recurso con estos identificadores",
"status": 409,
"details": {
"resource": "reservation",
"conflict": "duplicate_booking"
}
}
}
Errores de Lógica de Negocio
CENOTE_NOT_AVAILABLE
{
"success": false,
"error": {
"code": "CENOTE_NOT_AVAILABLE",
"message": "El cenote no está disponible para la fecha seleccionada",
"status": 422,
"details": {
"cenoteId": "c3n0t3-1234",
"date": "2024-01-15",
"reason": "mantenimiento"
}
}
}
INSUFFICIENT_CAPACITY
{
"success": false,
"error": {
"code": "INSUFFICIENT_CAPACITY",
"message": "No hay suficiente capacidad para la reservación solicitada",
"status": 422,
"details": {
"requested": 10,
"available": 5,
"cenoteId": "c3n0t3-1234",
"date": "2024-01-15"
}
}
}
Errores de Límite de Tasa
RATE_LIMIT_EXCEEDED
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Demasiadas peticiones. Por favor intenta más tarde",
"status": 429,
"details": {
"limit": 1000,
"window": "1 hora",
"resetTime": "2024-01-15T11:00:00Z"
}
}
}
Mejores Prácticas de Manejo de Errores
1. Siempre Verifica el Campo Success
// Ejemplo de TypeScript
const response = await client.cenotes.get(cenoteId);
if (!response.success) {
console.error("Error de API:", response.error);
// Maneja códigos de error específicos
switch (response.error.code) {
case "RESOURCE_NOT_FOUND":
// Maneja no encontrado
break;
case "RATE_LIMIT_EXCEEDED":
// Implementa estrategia de backoff
break;
default:
// Maneja error genérico
break;
}
return;
}
// Éxito - usa response.data
console.log(response.data);
2. Implementa Backoff Exponencial para Límites de Tasa
async function apiCallWithRetry<T>(
apiCall: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000
): Promise<T> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await apiCall();
} catch (error) {
if (error.status === 429 && attempt < maxRetries - 1) {
const delay = baseDelay * Math.pow(2, attempt);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
throw new Error("Máximo de reintentos excedido");
}
// Uso
const cenotes = await apiCallWithRetry(() =>
client.cenotes.list({ page: 1, perPage: 10 })
);
3. Valida la Entrada Antes de las Llamadas API
function validateReservationData(data: ReservationRequest): string[] {
const errors: string[] = [];
if (!data.cenoteId) {
errors.push("El ID del cenote es requerido");
}
if (!data.date || new Date(data.date) < new Date()) {
errors.push("Se requiere una fecha futura válida");
}
if (!data.visitors || data.visitors < 1) {
errors.push("Se requiere al menos un visitante");
}
return errors;
}
// Uso
const validationErrors = validateReservationData(reservationData);
if (validationErrors.length > 0) {
console.error("Errores de validación:", validationErrors);
return;
}
4. Maneja Errores de Red
import axios from "axios";
try {
const response = await client.cenotes.list();
} catch (error) {
if (axios.isAxiosError(error)) {
if (error.code === "ECONNREFUSED") {
console.error("Conexión rechazada - el servicio puede estar caído");
} else if (error.code === "ETIMEDOUT") {
console.error("Tiempo de espera agotado - intenta más tarde");
} else if (!error.response) {
console.error("Error de red:", error.message);
}
}
throw error;
}
5. Registra Errores para Depuración
interface ErrorLog {
timestamp: string;
requestId?: string;
endpoint: string;
method: string;
errorCode: string;
errorMessage: string;
context?: any;
}
function logError(error: any, context: Partial<ErrorLog>): void {
const errorLog: ErrorLog = {
timestamp: new Date().toISOString(),
requestId: error.requestId,
endpoint: context.endpoint || "desconocido",
method: context.method || "desconocido",
errorCode: error.code || "UNKNOWN_ERROR",
errorMessage: error.message || "Ocurrió un error desconocido",
context: context.context,
};
console.error("Error de API LosCenotes:", JSON.stringify(errorLog, null, 2));
// Envía a tu servicio de logging
// logger.error(errorLog);
}
Manejo de Errores por SDK
SDK de TypeScript
import { LosCenotesClient, LosCenotesError } from "@loscenotes/partner-sdk";
try {
const cenote = await client.cenotes.get("invalid-id");
} catch (error) {
if (error instanceof LosCenotesError) {
console.error("Error de LosCenotes:", {
code: error.code,
message: error.message,
status: error.status,
details: error.details,
});
} else {
console.error("Error inesperado:", error);
}
}
SDK de Python
from loscenotes import LosCenotesClient, LosCenotesError
try:
cenote = client.cenotes.get('invalid-id')
except LosCenotesError as e:
print(f"Error de LosCenotes: {e.code} - {e.message}")
print(f"Estado: {e.status}")
print(f"Detalles: {e.details}")
except Exception as e:
print(f"Error inesperado: {e}")
SDK de PHP
<?php
use LosCenotes\LosCenotesClient;
use LosCenotes\Exceptions\LosCenotesException;
try {
$cenote = $client->cenotes->get('invalid-id');
} catch (LosCenotesException $e) {
error_log("Error de LosCenotes: " . $e->getCode() . " - " . $e->getMessage());
error_log("Estado: " . $e->getStatus());
error_log("Detalles: " . json_encode($e->getDetails()));
} catch (Exception $e) {
error_log("Error inesperado: " . $e->getMessage());
}
?>
Probando Escenarios de Error
En Modo Sandbox
El entorno sandbox proporciona endpoints especiales para probar escenarios de error:
# Prueba error 404
curl -X GET https://service-gateway.loscenotes.com/v1/cenotes/test-not-found \
-H "Authorization: Bearer sk_test_your_api_key"
# Prueba error 429 de límite de tasa
curl -X GET https://service-gateway.loscenotes.com/v1/test/rate-limit \
-H "Authorization: Bearer sk_test_your_api_key"
# Prueba error de validación
curl -X POST https://service-gateway.loscenotes.com/v1/test/validation-error \
-H "Authorization: Bearer sk_test_your_api_key" \
-H "Content-Type: application/json" \
-d '{"invalid": "data"}'
Monitoreo de Errores
Configura Seguimiento de Errores
// Configura seguimiento de errores
function setupErrorTracking() {
// Ejemplo de Sentry
Sentry.init({
dsn: process.env.SENTRY_DSN,
beforeSend(event) {
// Filtra errores de API de LosCenotes que son esperados
if (event.tags?.errorCode === "RESOURCE_NOT_FOUND") {
return null;
}
return event;
},
});
}
// Rastrea errores de API
function trackApiError(error: LosCenotesError, context: any) {
Sentry.captureException(error, {
tags: {
errorCode: error.code,
apiEndpoint: context.endpoint,
apiMethod: context.method,
},
extra: {
requestId: error.requestId,
apiResponse: error.details,
},
});
}
Problemas Comunes y Soluciones
Problema: Errores 500 Intermitentes
Solución: Implementa lógica de reintento con backoff exponencial
const retryableStatusCodes = [500, 502, 503, 504];
if (retryableStatusCodes.includes(error.status)) {
// Reintenta con backoff exponencial
await retryWithBackoff(apiCall);
}
Problema: Autenticación Falla Repentinamente
Posibles Causas:
- Clave API expirada
- Permisos de clave API cambiados
- Desajuste de reloj (para firmas basadas en tiempo)
Solución:
if (error.code === "INVALID_API_KEY") {
// Verifica si la clave API necesita renovación
await renewApiKey();
// Reintenta la petición
return await retryRequest(originalRequest);
}
Problema: Fallas de Validación de Webhook
Solución:
// Asegura verificación adecuada de firma
function verifyWebhookSignature(payload: string, signature: string): boolean {
const secret = process.env.LOSCENOTES_WEBHOOK_SECRET;
const expectedSignature = crypto
.createHmac("sha256", secret)
.update(payload, "utf8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature, "hex"),
Buffer.from(expectedSignature, "hex")
);
}
Próximos Pasos
- Límites de Tasa - Comprende la limitación de tasa de la API
- Mejores Prácticas - Recomendaciones para producción