Si has hecho algún desarrollo web con Node en los últimos años, probablemente hayas utilizado Express. Incluso si no lo has usado directamente, muchos frameworks pensados para hacer el desarrollo web aún más simple están construidos sobre Express.
Una de las características clave en Express es la capacidad de crear rutas. Una combinación infinita de URLs puede llegar al mismo servidor Express, y las rutas son la forma de determinar qué URLs ejecutan qué pieza de código. Puedes tener parámetros y comodines para no tener que indicar explícitamente cada endpoint.
En este tutorial, te guiaré en la creación de un servidor y te enseñaré todo lo que necesitas saber sobre las rutas en Express.
¿Qué es una ruta en Express?
Las rutas determinan qué datos deben ser entregados dada cualquier URL. Tomemos como ejemplo el servidor de archivos más básico. Digamos que tienes una estructura de archivos de:
files/├── images/│ ├── cat.png│ ├── dog.jpg│ └── pig.bmp└── text/ ├── README.md └── todo.txt
Entonces podrías ejecutar un simple servidor HTTP que sirva automáticamente esos archivos y cree un índice para los directorios. No hay files/index.html
, pero el servidor sigue generando una página web y sirviendo contenido basado en los archivos de esa carpeta. Si vas a /images/cow.gif
obtendrás un error 404 – aunque no haya ningún archivo allí, sigue sirviendo algo.
npm install -g http-servercd fileshttp-server
En Express, una ruta está formada por un method
, un path
, y un handler
.
Métodos, rutas y manejadores, ¡vaya!
El method
puede ser cualquier verbo HTTP, como GET
(para obtener contenido – esto es lo que usan la mayoría de las páginas web), o POST
(para enviar contenido al servidor – esto es común con los formularios HTML). También puedes especificar que quieres que Express maneje la misma ruta para todos los métodos si así lo deseas.
El path
es una cadena o una expresión regular que describe la URL relativa. Si estás trabajando con la raíz de tu aplicación, esto describe la URL absoluta. Una ruta se puede definir de varias maneras.
El método y la ruta son esenciales para saber cuándo hacer algo, pero el manejador es la función de devolución de llamada que realmente se llama en esos casos. A un handler se le pasa un request
, un response
, y un next
callback, y esos argumentos se escriben típicamente como (req, res, next)
..
- Solicitud (
req
): La petición contiene todo tipo de información sobre lo que ha pedido el usuario. Desde aquí se puede acceder a la ruta, los parámetros, las cabeceras y un sinfín de cosas más. Para todo lo relativo a una petición, puedes consultar la referencia de la API - Respuesta (
res
): La respuesta es la forma de devolver la información al usuario. La forma más sencilla de devolver los datos es con el método.send
(por ejemplo,res.send('Hello, world!')
), pero hay muchos otros métodos. De nuevo, puedes encontrar todos los métodos en la referencia de la API - Siguiente Callback (
next
): La funciónnext
te permite utilizar múltiples manejadores para la misma ruta. Puedes usar un manejador para procesar información, y cuando haya terminado puede llamar anext()
para indicar que está bien pasar al siguiente manejador. Si pasas una cadena, en su lugar lanzará un error, que puedes atrapar en otro lugar, o mostrar al usuario (por ejemplo,next('You must be authenticated to access this route')
).
¿Qué es un router en Express?
Ahora que estás un poco más familiarizado con las rutas, ¿en qué se diferencian de un router? Puedes pensar en un router como una colección de rutas. Esto puede ser una forma útil de organizar las diferentes secciones de tu aplicación.
Cuando usas un router, puedes pensar en términos de una ruta raíz, incluso si vas a usar ese router desde alguna sub-ruta. Por ejemplo, digamos que tienes una API para gestionar mensajes. Podrías tener un router con una ruta '/'
a GET
todos los mensajes o POST
un nuevo mensaje. Podrías tener otra ruta '/:id'
para GET
o PUT
(editar) un mensaje específico.
Crear una aplicación sencilla con un router en Node con Express
Basta de hablar ya… vamos a por algo de código real. Para empezar, crea una carpeta que albergará todo tu código. A continuación, configura una carpeta package.json
para ayudar a gestionar las dependencias. Puedes usar npm init
para hacerlo. También necesitarás instalar Express.
mkdir my-first-routercd my-first-routernpm init -ynpm install [email protected] [email protected]
Crea un archivo index.js
con el siguiente código:
index.js
Esto le dice a Express que use Handlebars (hbs
) como motor de vista. Utiliza el path
incorporado en Node para indicarle el directorio que contiene las vistas. A la ruta /
se le dice que renderice la página usando index.hbs
, que pondrá el content
en una etiqueta de párrafo (p
).
Para asegurarte de que Express tiene plantillas para renderizar, crea una nueva carpeta llamada views
, y luego crea un nuevo archivo allí llamado layout.hbs
. Cuando le digas a Express que renderice una vista, primero renderizará layout.hbs
y pondrá el contenido de la vista dentro de la etiqueta {{{body}}}
. Esto te permite establecer un esqueleto para la aplicación. Aquí hay algo de HTML básico usando Bootstrap que te dará un buen estilo sin necesidad de escribir ningún CSS. Esto también renderizará el title
pasado al contexto en tu ruta /
.
views/layout.hbs
También tendrás que crear una vista index.hbs
que por ahora será muy básica:
views/index.hbs
<p>{{content}}</p>
Para facilitar un poco el desarrollo, puedes instalar nodemon
con:
npm install --save-dev [email protected]
Entonces modifica tu archivo package.json
para que la entrada "scripts"
incluya un script de inicio con nodemon .
. Esto hará que simplemente puedas ejecutar npm start
y tu servidor se reiniciará automáticamente cada vez que hagas cambios:
"scripts": { "start": "nodemon ."}
Ahora en tu terminal, si escribes npm start
arrancarás el servidor. Luego puedes ir a para ver la aplicación funcionando.
Crear un router en Express
Bueno, eso es un poco aburrido. Qué tal si hacemos que haga algo útil? Vamos a crear una simple lista de tareas. Empieza por crear un router para gestionar una lista de elementos. Haz un nuevo archivo llamado todo.js
:
todo.js
Aquí tienes dos manejadores de ruta. El primero escucha las peticiones de POST
(significadas por router.post
). Reemplazará la lista de tareas por una copia de lo que reciba del formulario. Si el formulario contiene la propiedad remove
(que contiene un índice), utilizará splice
para eliminar el elemento en ese índice. Si el formulario contiene la propiedad new
, se introducirá un nuevo elemento en el array. Una vez que ha terminado de modificar la lista de tareas, llama a next()
para pasar al siguiente manejador de ruta.
El segundo manejador de ruta se utiliza siempre (señalado por router.use
). Su único propósito es renderizar la lista de tareas. Al separar las rutas de esta manera, puedes hacer fácilmente una cosa siempre, y otra cosa sólo en ciertas circunstancias (en este caso en una solicitud POST
).
Para decirle a la aplicación que use este router, tendrás que añadir unas líneas a index.js
:
index.js
Ahora a la plantilla todo
. Es un poco más grande, así que la he dejado para el final. Si estás familiarizado con HTML no debería ser tan malo de seguir. Handlebars añade algunas características que te permiten acceder a las variables. En este caso, estás utilizando un bloque {{#if}}
para mostrar algo especial si no hay ningún elemento, así como un bloque {{#each}}
para mostrar cada uno de los elementos de la lista con un marcado mínimo.
El único JavaScript que se utiliza aquí es para enviar automáticamente el formulario cuando se cambia algo. Si se deshabilitara JavaScript, esto seguiría funcionando al pulsar la tecla «Enter» del teclado, gracias al botón oculto etiquetado como «Autosave».
views/todo.hbs
Ahora ve a e introduce algunos elementos en tu lista de tareas pendientes.
Añadir autenticación de usuario en Node
Ahora tienes una lista de tareas pendientes funcional. Sin embargo, habrás notado que esto sólo funcionaría si quieres que todos los que la usen compartan la misma lista. Si añades la autenticación, puedes tener una lista de tareas separada para cada usuario.
Añadir usuarios no tiene por qué ser un dolor. De hecho, se puede hacer de forma realmente sencilla con Okta. Qué es Okta?», te preguntarás. Okta es un servicio en la nube que permite a los desarrolladores crear, editar y almacenar de forma segura cuentas de usuario y datos de cuentas de usuario, y conectarlos con una o varias aplicaciones.
Si aún no tienes una, regístrate para obtener una cuenta de desarrollador gratuita para siempre.
Vas a necesitar guardar algo de información para utilizarla en la app. Crea un nuevo archivo llamado .env
. En él, introduce la URL de tu organización.
HOST_URL=http://localhost:3000OKTA_ORG_URL=https://{yourOktaOrgUrl}
También necesitarás una cadena aleatoria para usarla como App Secret para las sesiones. Puede generarla con los siguientes comandos:
echo -e "\nAPP_SECRET=`npx -q uuid`" >> .env
A continuación, inicie sesión en su consola de desarrollador, navegue hasta Aplicaciones y haga clic en Añadir aplicación. Seleccione Web, y luego haga clic en Siguiente. Dale a tu aplicación un nombre, como «Mi primer router». Cambia el URI base a y el URI de redirección de inicio de sesión a
, luego haz clic en Hecho
Haz clic en Editar y añade un URI de redirección de cierre de sesión de , luego haz clic en Guardar.
La página a la que llegas después de crear una aplicación tiene algo más de información que necesitas guardar en tu archivo .env
. Copia el ID de cliente y el secreto de cliente.
OKTA_CLIENT_ID={yourClientId}OKTA_CLIENT_SECRET={yourClientSecret}
Ahora vuelve al código. Tendrás que añadir el middleware OIDC de Okta para controlar la autenticación. También se basa en el uso de sesiones. Necesitarás usar dotenv
para leer las variables del archivo .env
. Para instalar las dependencias que necesitarás, ejecuta este comando:
npm install @okta/[email protected] [email protected] [email protected]
Ahora modifica tu archivo index.js
. Aquí añadirás los middlewares de sesión y OIDC, y una ruta logout
para que los usuarios puedan cerrar la sesión de la app. También estás añadiendo un middleware específicamente al todoRouter
app.use('/todo', oidc.ensureAuthenticated(), todoRouter)
). Al añadir oidc.ensureAuthenticated()
, estás dejando que Okta se asegure de que no se puede acceder a esa ruta a menos que el usuario haya iniciado la sesión. Si el usuario no ha iniciado la sesión y trata de llegar a esa ruta, será llevado a un sitio seguro para iniciar la sesión, y será redirigido de nuevo a tu sitio después.
index.js
Para hacer las cosas un poco más fáciles cuando un usuario cierra la sesión, añade un enlace a la lista de tareas desde la página de inicio.
views/index.hbs
<p>{{content}}</p><a href="/todo">Go to To-Do List</a>
También puedes añadir un mensaje de bienvenida y un botón de cierre de sesión a tu layout.hbs
.
views/layout.hbs
Para que eso funcione, tendrás que añadir userinfo
al contexto cuando se rendericen las vistas.
todo.js
index.js
Ok, así que ahora estás requiriendo que los usuarios inicien sesión antes de que puedan editar la lista de tareas, pero sigue siendo una lista única y compartida. Para dividirla en una lista separada para cada usuario, haz otro pequeño cambio en todo.js
.
todo.js
Aprende más sobre Node, Express y el desarrollo web seguro
Ahora que tienes una lista de tareas totalmente funcional, te animo a ampliarla. Intenta almacenar los datos en una base de datos, ¡o incluso deja que Okta los almacene por ti! Mira si puedes crear algunos routers más para añadir al servidor web.
Si quieres ver el ejemplo de código final, puedes encontrarlo en GitHub.
Si quieres aprender más sobre Node y Express echa un vistazo a algunos de estos otros artículos en el blog de desarrolladores de Okta:
- Construye y entiende el middleware Express a través de ejemplos
- Construye y entiende un simple sitio web Node.js con autenticación de usuarios
- Construir una API REST simple con Node y OAuth 2.0
- Construir una autenticación de Node segura con Passport.js y OpenID Connect
- Asegurar una API de Node con credenciales de cliente OAuth 2.0