Manejando peticiones en NodeJS

Tenemos que crear un manejador de solicitudes, o Request Handler, que nos permita desencadenar acciones dependiendo de la URL que recibamos.

Por ejemplo, supongamos que estamos desarrollando un sitio web, el cual tiene las siguientes secciones:

  • index
  • contact
  • error

Es una estructura bastante básica de un sitio, en la cual index es la landing page, contact un formulario de contacto básico, y error es la pantalla 404 que se lanza cuando la ruta solicitada no existe.

Lo lógico seria que al ingresar a nuestro sitio, por ejemplo, localhost:3000/contact, nos mostrara el formulario de contacto, y que cuando ingresáramos a una URL que no existiera, por ejemplo localhost:3000/pepito, nos devolviera un 404 y la pantalla de error.

Partiendo del código anterior, ya somos capaces de poder leer la URL e interpretar cual es la solicitud, pero no de reaccionar ante estas solicitudes. ☹️

Creemos un manejador de peticiones básico

Actualmente, nuestro router.js utiliza la función route() para obtener la URL que se esta enviando desde el cliente, y que hacer al respecto; en este caso un mensaje de log sencillo.

En el caso de que el cliente solicitara la ruta /contact, lo lógico seria que se renderizara el formulario de contacto, y al ir a la ruta /, la landing page. Actualmente, no importa la petición que llegue, simplemente sigue mostrando el mensaje de "Hola Mundo".

Quizá, podría parecer una buena idea el sobrecargar dicho método con la lógica de negocio, y mediante el uso de estructuras tipo ifs o switch decidir desencadenar eventos dependiendo de la ruta solicitada, algo como esto:

exports.route = (pathname) => {  
    if(pathname === '/') {
        console.log('Solicitud sobre ruta: ' + pathname)
        //Evento desencadenado en la ruta '/'
    } else if (pathname === '/contact'){
        console.log('Solicitud sobre ruta: ' + pathname)
        //Evento desencadenado en la ruta '/contact'
    } else {
        console.log('404 Not Found')
    }
}

Claro, para nuestra app de ejemplo, utilizar este código podría ser valido, es mas, funciona correctamente (animo a probarlo), pero es indudablemente un error y una practica de programación horrible.
No solo no es escalable, sino que cada vez que a nuestra app se le tenga que agregar una nueva ruta, tendremos que abrir esta maraña de código que se va a terminar formando.
Tampoco es mantenible, bajo ningún concepto, se imaginan tener que debugear un código en un infierno de ifs? Mejor no imaginarlo.

Por este motivo, es una excelente idea que esta lógica este en otro lugar, en otro modulo, y que a su vez utilice alguna forma mas "elegante"... 🤔🤔🤔🤔

Si nos abstraemos un poco, es fácil darse cuenta de que las peticiones no son otra cosa mas que una URL asociada a un String.

Por ejemplo:

Por lo tanto, quizá podríamos utilizar un vector asociativo para manejar dichas solicitudes, no obstante hay un problema, JavaScript no tiene vectores asociativos. 😡

La solución a este inconveniente técnico, se encuentra en el mismo diseño de JavaScript como lenguaje de programación.
A diferencia de otros lenguajes de programación, en JS, los objetos son colecciones de pares clave/valor... 😧

Pasemos un poco en limpio entonces todo lo que escribí anteriormente. En esencia, necesitamos un nuevo modulo que maneje estas peticiones, un requestHandlers.js. A grandes rasgos, se podría decir que el modulo router.js, interpreta las rutas, y el modulo requestHandlers.js las maneja.

Su código, como se vera, es muy sencillo y solo se limita a devolver (por ahora) un mensaje de log que indica que se ha llamado a dicho manejador, de dicha petición.

//requestHandlers.js

exports.getIndex = () => {  
    console.log('Manejador de solicitud "index" solicitado')
}

exports.getContact = () => {  
    console.log('Manejador de solicitud "contact" solicitado')
}

Este nuevo modulo, lo que nos va a permitir es comunicar nuestros manejadores de peticiones con nuestro enrutador, de modo de que este sea reutilizable.

Para poder utilizarlo, debemos importarlo en nuestro app.js

import requestHandlers from './requestHandlers'  

Y aplicar algunas modificaciones, de modo que asociemos a cada manejador una acción (o evento 😉) concreto:

const handle = {  
  '/': requestHandlers.getIndex,
  '/contact': requestHandlers.getContact
}

Acá, se puede apreciar el objeto de claves nombre/valor del que hable un poco mas arriba.

Finalmente, pero no menos importante, debemos enviar este objeto handle como parámetro de nuestra funciona startServer(), de otro modo, no podríamos utilizarlo y carecería de sentido.

server.startServer(router.route, handle)  

De esta forma, tenemos un código mucho mas limpio tanto estética como funcionalmente y ni hablemos de la escalabilidad, ya que es mucho mas fácil agregar una nueva par clave/valor en el objeto antes de estar renegando con sentencias ifs, no?

Debemos extender la funcionalidad de nuestro server.js.

Primero, la función startServer() ahora debe recibir aparte de la función route(), al objeto handle:

exports.startServer = (route, handle) => {  

Al llamar al método route(), aparte del pathname, le pasamos como parámetro el objeto handle

route(handle, pathname)  

Con esto, tenemos conectado nuestro router.js, servidor y nuestro manejador de peticiones, pero aun no esta haciendo nada realmente.

Para finalizar, vamos a extender la funcionalidad de nuestro router.js, de la siguiente forma:

exports.route = (handle, pathname) => {  
    console.log('Solicitud sobre ruta: ' + pathname)
    if (typeof handle[pathname] === 'function') {
        return handle[pathname]()
    } else {
        console.log("No se encontro manipulador para " + pathname);
        return "404 No Encontrado";
    }
}

Acá, lo que hace es comprobar si el pathname que obtiene desde el objeto url, corresponde a alguna ruta que el manejador conozca, en ese caso retorna la función de dicho manejador (su lógica de negocio), y en caso contrario, devuelve un 404 ya que ese recurso no lo encuentra.

Como se puede ver en la imagen, la solicitudes para / y /contact las conoce, no obstante, la solicitud para /pepe es desconocida y por eso devuelve el mensaje de error.

Como siempre, todo el código que escribí en este articulo, se puede encontrar en mi cuenta de GitHub, en el siguiente link.

Hasta la próxima entrada! 😬

Show Comments

Get the latest posts delivered right to your inbox.