Ya sea que esté realizando llamadas a la API desde Node.js o en el navegador, eventualmente se producirán fallas en la conexión. Algunos errores de solicitud son válidos. Tal vez el punto final estaba equivocado o el cliente envió los datos incorrectos. Otras veces puede estar seguro de que el error es el resultado de un problema con la conexión al servidor o uno de los muchos saltos intermedios. Si bien el monitoreo de la API y el servicio web puede informarle sobre el problema, una solución más activa puede solucionarlo por usted. Para solucionar esto, puede mejorar su biblioteca de solicitudes HTTP agregando la funcionalidad de reintento inteligente. Este tipo de remediación es crucial para garantizar que sus llamadas API sean exitosas. Algunas bibliotecas, como la compatibilidad el reintento de solicitudes fallidas de forma inmediata, mientras que otras, como requieren un complemento separado. Si su biblioteca preferida no los admite, este artículo es para usted. Exploraremos la adición de reintentos específicos del código de estado a una solicitud, haciéndolos cada vez más largos con una técnica llamada "retroceso", y más. con axios, Los fundamentos de un reintento Para decidir cuándo volver a intentar una solicitud, debemos considerar qué buscar. Hay un puñado de que puede verificar. Esto permitirá que su lógica de reintento diferencie entre una solicitud fallida que es apropiada para reintentar, como un error de puerta de enlace, y otra que no lo es, como un 404. Para nuestros ejemplos, usaremos 408, 500, 502, 503, 504, 522 y 524. También puede verificar , siempre que incorpore el encabezado Reintentar después en la lógica de retroceso. códigos de estado HTTP 429 La siguiente consideración que queremos es con qué frecuencia volver a intentarlo. Comenzaremos con un retraso, luego lo aumentaremos cada vez más. Este es un concepto conocido como "retroceso". El tiempo entre solicitudes crecerá con cada intento. Finalmente, también tendremos que decidir cuántos intentos hacer antes de rendirnos. Aquí hay un ejemplo de la lógica que usaremos en pseudocódigo: Si el total de intentos > intentos, continuar si el tipo de código de estado coincide, continúe si (ahora - retrasar)> último intento, intentar solicitud de lo contrario, volver al principio También podríamos verificar cosas como códigos de error (de Node.js) y limitar los reintentos a ciertos métodos. Por ejemplo, ignorar POST suele ser una buena idea para garantizar que no se creen entradas duplicadas. Estructura de solicitud recursiva Para que todo esto funcione, realizaremos una solicitud desde una solicitud fallida. Esto requiere el uso de la recursividad. La recursividad es cuando una función se llama a sí misma. Por ejemplo, si quisiéramos seguir intentando realizar una solicitud infinitamente, podría verse así: { requests(url, options, response => { (response.ok) { response } { myRequest(url, options) } }) } ( ) function myRequest url, options = {} return if return else return Note que el bloque devuelve el función. Dado que la mayoría de las implementaciones modernas de solicitudes HTTP se basan en promesas, podemos devolver el resultado. Esto significa que, para el usuario final, todo el proceso parece una llamada normal. Por ejemplo: else myRequest myRequest( ).then( .log(response)) "https://example.com" console Con un plan en mente, veamos cómo implementar reintentos en javascript. Agregar reintento para obtener Primero, comenzaremos con la del navegador. La implementación de obtención será similar al ejemplo de recursividad anterior. Implementemos ese mismo ejemplo, pero usando fetch y una verificación de estado. API Fetch { fetch(url, options).then( { (res.ok) res.json() fetchRetry(url, options) }) } ( ) function fetchRetry url, options // Return a fetch request return => res // check if successful. If so, return the response transformed to json if return // else, return a call to fetchRetry return Esto funcionará para reintentar infinitamente las solicitudes fallidas. . Nota: un retorno saldrá del bloque actual, por lo que no necesitamos una declaración else después de return res.json() Ahora agreguemos un número máximo de reintentos. { fetch(url, options) .then( { (res.ok) res.json() (retries > ) { fetchRetry(url, options, retries - ) } { (res) } }) .catch( .error) } ( ) function fetchRetry url, options = {}, retries = 3 return => res if return if 0 return 1 else throw new Error console El código es casi el mismo, excepto que agregamos un nuevo argumento y una nueva condición. Añadir el re ries argumento a la función, con un valor predeterminado de 3. Luego, en lugar de llamar automáticamente a la función en caso de falla, verifique si quedan reintentos. Si es así, llame . t fetchRetry El nuevo valor de reintentos pasado al siguiente intento es el actual reintentos menos 1. Esto asegura que nuestro "bucle" disminuya y finalmente se detenga. Sin esto, se ejecutaría infinitamente hasta que la solicitud tenga éxito. Finalmente, si no es mayor que cero, lanza un nuevo error para manejar. retries .catch Para probarlo, puede hacer una solicitud a . Por ejemplo: https://status-codes.glitch.me/status/400 fetchRetry( ) .then( .log) .catch( .error) "https://status-codes.glitch.me/status/400" console console Si verifica el tráfico de su red, debería ver llamadas en total. El original, más tres reintentos. A continuación, agreguemos una verificación de los códigos de estado que queremos volver a intentar. cuatro { retryCodes = [ , , , , , , ] fetch(url, options) .then( { (res.ok) res.json() (retries > && retryCodes.includes(res.status)) { fetchRetry(url, options, retries - ) } { (res) } }) .catch( .error) } ( ) function fetchRetry url, options = {}, retries = 3 const 408 500 502 503 504 522 524 return => res if return if 0 return 1 else throw new Error console Primero, declare una matriz de códigos de estado que queremos verificar. También podría agregar esto como parte de la configuración, especialmente si implementó esto como una clase con una configuración más formal. A continuación, la condición de reintento verifica si el estado de la respuesta existe en la matriz utilizando . Si es así, intente la solicitud. Si no, lanza un error. ECMAScript. array.includes() Hay una última característica para agregar. El retardo de retroceso incremental entre cada solicitud. Vamos a implementarlo. { retryCodes = [ , , , , , , ] fetch(url, options) .then( { (res.ok) res.json() (retries > && retryCodes.includes(res.status)) { setTimeout( { fetchRetry(url, options, retries - , backoff * ) }, backoff) } { (res) } }) .catch( .error) } ( ) function fetchRetry url, options = {}, retries = , backoff = 3 300 /* 1 */ const 408 500 502 503 504 522 524 return => res if return if 0 => () /* 2 */ return 1 2 /* 3 */ /* 2 */ else throw new Error console Para manejar la mecánica de "esperar" antes de volver a intentar la solicitud, puede usar setTimeout. Primero, agregamos nuestro nuevo argumento de configuración (1). Luego, configure setTimeout y use el valor de retroceso como retraso. Finalmente, cuando ocurre el reintento, también pasamos el back-off con un modificador. En este caso, backoff * 2. Esto significa que cada nuevo intento esperará el doble que el anterior. Ahora, si probamos la función llamando a fetchRetry('https://status-codes.glitch.me/status/500'), el código realizará la primera solicitud de inmediato, el primer reintento después de esperar 300 ms, los siguientes 600 ms después la primera respuesta y el intento final 900 ms después de la segunda respuesta. Puede probarlo con cualquier código de estado usando https://status-codes.glitch.me/status/${STATUS_CODE}. Más configuraciones y mejores opciones Esta es una gran solución para solicitudes únicas o aplicaciones pequeñas, pero para implementaciones más grandes, podría mejorarse. La creación de una clase configurable (u objeto similar a una clase) le dará más control y permitirá configuraciones separadas para cada integración de API. También podría aplicar esta lógica a un o cualquier otro patrón de remediación. disyuntor Otra opción es utilizar una herramienta que observe y reaccione ante anomalías en sus llamadas API. En , nuestro equipo está construyendo precisamente eso. En lugar de configurar todo esto en código para cada API, el Bearer Agent lo maneja todo por usted. hoy y háganos saber lo que piensa Bearer Pruébelo @BearerSH Bonificación: Agregar reintento al nativo de Node módulo http La implementación de búsqueda anterior funciona para el navegador, pero ¿qué pasa con Node.js? Podría usar una biblioteca equivalente a fetch como . Para hacer las cosas interesantes, veamos la aplicación de los mismos conceptos anteriores al nativo de Node.js. módulo. node-fetch http Para hacer las cosas un poco más fáciles, usaremos la abreviatura método. La lógica de reintento seguirá siendo la misma, así que consulte nuestro artículo sobre cómo si desea realizar solicitudes que no sean GET. http.get realizar llamadas API con http.request Antes de comenzar, necesitaremos cambiar http.get de basado en eventos a basado en promesas para que podamos interactuar con él de la misma manera que lo hicimos con fetch. Si es nuevo en las promesas, son un concepto subyacente que usan las implementaciones asincrónicas modernas. Cada vez que usas o , está usando promesas bajo el capó. Para los propósitos de este artículo, todo lo que necesita saber es que una promesa puede resolverse o rechazarse; en otras palabras, el código pasa o falla. Veamos un código sin ninguna lógica de reintento. .then async/await Aquí hay un GET básico usando http.get https = ( ) https.get(url, res => { data = { statusCode } = res (statusCode < || statusCode > ) { (res) } { res.on( , d => { data += d }) res.end( , () => { .log(data) }) } }) let require "https" let "" let if 200 299 throw new Error else "data" "end" console Para resumir, solicita una url. Si el no está en un "rango de éxito" definido (Fetch tiene el propiedad para manejar esto) arroja un error. De lo contrario, genera una respuesta y se registra en la consola. Veamos cómo se ve esto "prometido". Para que sea más fácil de seguir, omitiremos parte del manejo de errores adicional. statusCode ok { ( { https.get(url, res => { data = { statusCode } = res (statusCode < || statusCode > ) { reject( (res)) } { res.on( , d => { data += d }) res.on( , () => { resolve(data) }) } }) }) } ( ) function retryGet url return new Promise ( ) => resolve, reject let "" const if 200 299 Error else "data" "end" Las partes clave aquí son: Devolviendo una nueva Promesa sobre acciones exitosas resolver por errores rechazar Entonces podemos probarlo llamando . Cualquier cosa fuera del rango de 200 aparecerá en nuestra captura, mientras que cualquier cosa dentro del rango aparecerá en ese momento. retryGet("https://status-codes.glitch.me/status/500").then(console.log).catch(console.error) A continuación, traigamos toda la lógica del ejemplo de búsqueda a . retryGet { retryCodes = [ , , , , , , ] ( { https.get(url, res => { data = { statusCode } = res (statusCode < || statusCode > ) { (retries > && retryCodes.includes(statusCode)) { setTimeout( { retryGet(url, retries - , backoff * ) }, backoff) } { reject( (res)) } } { res.on( , d => { data += d }) res.on( , () => { resolve(data) }) } }) }) } ( ) function retryGet url, retries = , backoff = 3 300 /* 1 */ const 408 500 502 503 504 522 524 /* 2 */ return new Promise ( ) => resolve, reject let "" const if 200 299 if 0 /* 3 */ => () return 1 2 else Error else "data" "end" Esto es similar a la ejemplo. Primero, configure los nuevos argumentos (1). Luego, defina la . Finalmente, configure la lógica de reintento y . Esto asegura que cuando el usuario llama y espera que le devuelvan una promesa, la recibirán. fetch retryCodes (2) regrese retryGet retryGet(...) Terminando ¡Te quedaste con él a través de la sección de bonificación 🎉! Con los mismos conceptos de este artículo, puede aplicar la funcionalidad de reintento a su biblioteca favorita si aún no la incluye. ¿Buscas algo más sustancioso? Pruebe Bearer y consulte el Blog de Bearer para obtener más información sobre Node.js, integraciones de API, mejores prácticas de monitoreo y más. Publicado anteriormente en https://blog.bearer.sh/add-retry-to-api-calls-javascript-node/