Inicio / Artículos / 12 reglas prácticas para API fiables de Node.js

12 reglas prácticas para API fiables de Node.js

Reglas prácticas de fiabilidad para API en Node.js: tiempos de espera, idempotencia, transacciones, invalidación de caché, registro de eventos y latencia en las dependencias, antes de la página de las 3 a.m.

906 palabras

Algunas lecciones sobre fiabilidad solo se graban después de una interrupción. Las doce que se mencionan a continuación ocurren con suficiente frecuencia, en suficientes servicios Node.js, como para que sea más económico exponerlas claramente en lugar de tener que descubrirlas nuevamente bajo presión. Trátelas como valores por defecto al diseñar los manejadores, y no como mejoras opcionales después del lanzamiento.

1. Las solicitudes reintentables se volverán a intentar.

No “podrían”. Los clientes se agotan en el tiempo de espera, las conexiones se interrumpen a mitad de la respuesta y la gente envía solicitudes repetidamente. Los balanceadores de carga y las redes móviles añaden sus propias entregas duplicadas. Cualquier manejador que cree o modifique estado necesita una respuesta explícita para “¿qué pasa si este mismo contenido llega de nuevo?”, porque en un plazo lo suficientemente largo eso no es un caso extremo: es inevitable. Diseñe tanto el camino óptimo como el camino de duplicados al mismo tiempo.

2. Asigne un tiempo de espera a cada llamada saliente.

Consultas a la base de datos, HTTP de terceros, pasarelas de pago: si se omite un plazo límite, un socio con problemas puede dejar el socket abierto indefinidamente. Un simple fetch(url) no es “paciente durante un tiempo”; se trata de una espera ilimitada, y las esperas ilimitadas permiten que una dependencia lenta congele todo lo anterior en la cadena de procesos. Es preferible utilizar los valores predeterminados de las bibliotecas que impongan un límite, y generar errores visibles para el usuario o intentos de reintento en lugar de bloqueos silenciosos.

3. Las verificaciones en el navegador son parte de la experiencia de usuario, no una barrera de seguridad.

Los usuarios pueden omitir tu SPA e interactuar directamente con la API utilizando JSON arbitrario a través de curl, Postman o un script comprometido. Las reglas importantes —correo electrónico bien formado, precio no negativo, campos obligatorios— deben aplicarse nuevamente en el servidor con la misma severidad que se usaría contra tráfico malintencionado. La validación en el cliente solo ayuda a usuarios cooperativos que de todos modos no violarían los acuerdos establecidos.

4. Una caché sin un mecanismo de invalidación es un error diferido.

El almacenamiento en caché crea una segunda versión de la verdad, y estas versiones pueden divergir. Al implementar la caché, decida qué escritura, webhook o evento TTL la vaciará y documente esa decisión junto a la clave de caché. Confiar en que “se vence en algún momento” suele significar “después de un tiempo mayor del que los clientes están dispuestos a tolerar saldos incorrectos”, especialmente cuando los TTL se miden en horas por motivos de “rendimiento”.

5. Las escrituras acopladas requieren una transacción, no optimismo.

await update1(); await update2(); parece atómico, pero no lo es. Entre los awaits, una caída, un problema temporal o un rechazo pueden hacer que solo se aplique una de las actualizaciones, lo que provoca que el inventario y la facturación se desalineen. Solo una verdadera transacción de base de datos, o una saga explícita con acciones compensatorias, puede cerrar ese período con garantías sobre las que se pueda razonar.

6. Herramienta para el incidente que aún no has visto.

Cuando algo se rompe, ya es demasiado tarde para añadir la línea de registro que podría haberlo explicado. Los registros estructurados, los IDs de correlación que se transmiten entre servicios y el contexto suficiente para reconstruir una solicitud antes de que ocurra la falla superan a cualquier sesión de depuración posterior, por más ingeniosa que sea. Los datos de registro que se pueden conservar con seguridad, los resultados y las latencias de dependencia mientras el sistema está en buen estado.

7. La memoria del proceso es un caché, nunca el sistema de registro permanente.

Un valor que solo existe en un proceso Node desaparece al reiniciarse, es invisible para las instancias hermanas y nunca fue duradero. Las banderas de funcionalidad almacenadas en RAM, los contadores o los Maps “temporales” comparten ese mismo destino. Si algo debe sobrevivir a una implementación, una caída o un equilibrio de carga sin sesiones persistentes, guárdalo en una base de datos u otro almacenamiento duradero, no en un slot de RAM aleatorio.

8. Planifique para que cada llamada a la red falle eventualmente.

La pregunta útil no es “¿esto suele funcionar?”, sino “¿qué ocurre en los casos raros de fallo?”. Los tiempos de espera, las respuestas parciales y los picos de errores 5xx cuentan todos. Si la respuesta honesta es “no está claro”, cierre esa brecha con intentos de reintentar seguros, interruptores de circuito o modos degradados antes de que la producción descubra el problema.

9. Una cola reprograma el trabajo; no lo elimina.

Al poner algo en cola parece que se ha completado. Un procesador seguirá realizando el mismo trabajo más tarde, con los mismos modos de fallo, solo pospuestos en el tiempo. Si los procesadores se retrasan, la acumulación de tareas crece aunque la API siga siendo receptiva; es mejor que bloquear solicitudes, pero no implica un trabajo menos costoso. Defina el tamaño de los consumidores y las rutas de correos no entregados con la misma cuidado que el tamaño del nivel HTTP.

10. Elija nombres pensados para lectores a seis meses, no como atajo para hoy.

data, temp y handleStuff mantienen su precisión solo hasta que otros tres conceptos también califiquen como “data”. Los nombres relacionados con la idea del negocio sobreviven ante lectores sin contexto alguno, incluido el tú futuro en un servicio desconocido. Prefiera sustantivos relacionados con el dominio sobre verbos mecánicos que describan los detalles iniciales de implementación.

11. Los creadores serán llamados dos veces.

Redes inestables, clientes de reintentos agresivos, toques dobles impacientes: cualquiera de ellos. Las solicitudes POST de creación requieren un manejo adecuado de duplicados, generalmente mediante una clave de idempotencia almacenada junto con el recurso resultante; de lo contrario, los cargos duplicados y los pedidos repetidos se convierten en un problema de sincronización. Asegúrese de que la condición “ya creado” devuelva el resultado original en lugar de un nuevo efecto secundario.

12. La latencia del endpoint sigue a la dependencia más lenta.

Un manejador elegante que realiza una consulta no indexada o llama a una API remota sin tiempo de espera sigue siendo lento. Analizar las propias líneas de código mientras se ignoran los llamados recibidos responde a la pregunta incorrecta. Mida los valores p95 y p99 en todo el grafo de llamadas, y luego corrija los índices, el procesamiento por lotes y los tiempos de espera en los puntos donde realmente se gasta el tiempo.

Ninguno de estos consejos es ingenioso, y ese es precisamente el punto: las soluciones ideadas a altas horas de la noche rara vez lo son. Por lo general, se trata de uno de estos doce problemas que se dejan sin atención hasta que ya no parecen urgentes. Implemente las medidas de seguridad necesarias mientras el servicio sigue siendo lo suficientemente pequeño como para modificarlo.

Lecturas relacionadas