Introducción a node.js y express
express
Es un framework de aplicación web de back-end para node.js.
En sitios web o aplicaciones web dinámicas, que accedan a bases de datos, el servidor espera a recibir peticiones HTTP del navegador (o cliente). Cuando se recibe una petición, la aplicación determina cuál es la acción adecuada correspondiente, de acuerdo a la estructura de la URL y a la información (opcional) indicada en la petición con los métodos POST o GET. Dependiendo de la acción a realizar, puede que se necesite leer o escribir en la base de datos, o realizar otras acciones necesarias para atender la petición correctamente. La aplicación ha de responder al navegador, normalmente, creando una página HTML dinámicamente para él, en la que se muestre la información pedida, usualmente dentro de un elemento especifico para este fin, en una plantilla HTML. Express posee métodos para especificar qué función ha de ser llamada dependiendo del verbo HTTP usado en la petición (GET, POST, SET, etc.) y la estructura de la URL ("ruta"). También tiene los métodos para especificar que plantilla (view) o gestor de visualización utilizar, donde están guardadas las plantillas de HTML que han de usarse y como generar la visualización adecuada para cada caso. El middleware de express, puede usarse también para añadir funcionalidades para la gestión de cookies, sesiones y usuarios, mediante el uso de parámetros, en los métodos POST/GET. Puede utilizarse además cualquier sistema de trabajo con bases de datos, que sea soportado por node.js (express no especifica ningún método preferido para trabajar con bases de datos)1.
La primera aplicación de Hola mundo codificada haciendo uso de express quedaría:
const express = require('express'); const app = express(); app.get(/./, (req, res) => { res.send("¡Hola mundo!"); }) app.listen(8080, () => { console.log('servidor funcionando en puerto 8080'); })
Las dos primeras líneas importan el paquete express y crean la aplicación express. A partir de este momento todo se hará a través de esta aplicación. En las líneas 4 y 5 vemos como se simplifica el tema del enrutado y del método de petición que se vio antes. El método get responderá a cualquier petición realizada mediante el método GET. El primer argumento del método get es la ruta a la que responderá. Puede ser una cadena, en la que se admiten determinados caracteres comodín, o como en el ejemplo, una expresión regular. La expresión regular /./ coincidirá con cualquier carácter por lo que cualquier petición mediante el método GET a cualquier dirección será respondida por la función de callback que aparece en el segundo argumento pasándole como argumentos el objeto request y response que ya se vieron anteriormente. Los objetos request y response no son los mismos que en node.js. Son objetos mejorados y además de mantener toda la funcionalidad, propiedades y métodos de node.js incorporan nuevos métodos y propiedades proporcionados por express. En las últimas líneas se pone en escucha en el puerto 8080 la aplicación tras la que subyace un servidor http.
Existen métodos en el objeto aplicación de express que responden a todos los métodos de envío de petición, siendo los más habituales get, post, delete, put, existiendo también un método para responder a cualquier tipo de petición, el método all.
El ejemplo en que se establecía un routing a tres páginas: la raíz /, /about y /data y en que esta última aceptaba peticiones mediante GET y POST quedaría codificado haciendo uso de express:
const express = require('express'); const fs = require("fs"); const app = express(); app.use(express.urlencoded({ extended: true })); app.get("/", (req, res) => { res.send("Home"); }); app.get("/about", (req, res) => { res.send("Prueba de routing"); }); app.get("/data", (req, res) => { fs.readFile("formulario.html", function(err, data) { if (err) { res.status = 404; res.send("Página no encontrada"); } else { res.setHeader('Content-type', 'text/html'); res.send(data); } }); }) app.post("/data", (req, res) => { res.send("<p>Recibido:</p><p>nombre:" + req.body.nombre + "</p><p>password:" + req.body.password + "</p>"); }); app.use((req, res, next) => { res.status = 404; res.send("recurso no encontrado"); }); app.listen(8080, () => { console.log('servidor funcionando en puerto 8080'); })
La cuarta línea de código ejecuta el método use2 del objeto de aplicación express, pasándole como argumento el resultado de invocar el método urlencoded del objeto express. El método use sirve para ejecutar la función o funciones especificadas en el/los argumentos del mismo que se denomina middleware3 . Un middleware no es más que una función de callback que tiene acceso a los objetos request, response y la siguiente función de middleware en el ciclo de solicitud-respuesta de la aplicación, normalmente referido como argument next. Más adelante se verán ejemplos de utilización de middlewares. El método use puede recibir como primer argumento opcional el path de routing en el que se aplicará la carga del middleware. El path, ya se ha comentado, puede ser una cadena, por ejemplo “/data” con determinados caracteres comodín como * o ?, o puede ser una expresión regular. En ambos casos se aplicará a paths que comiencen por la cadena o expresión regular, así “/data” se aplicará a todos los paths que comiencen por /data (en los métodos de verbo get, post, delete etc la coincidencia de la ruta es exacta y por lo tanto “/data” coincide únicamente con /data). Si se omite este primer argumento se aplicará a todos los path, como ocurre en nuestro ejemplo.
El middleware urlencoded analiza el cuerpo de la petición (body) y busca parámetros codificados en él haciéndolos accesibles a través del objeto body propiedad del objeto request con nombre el del parámetro codificado. En nuestro ejemplo, req.body.nombre hace referencia al parámetro nombre enviado en la solicitud POST en la ruta /data.
Los middleware que se carguen con use o con los verbos de método get, post, … se ejecutarán en orden secuencial según estén escritos en el código, con alguna salvedad: si la función de callback termina la petición, por ejemplo, ejecutando send, ya no se ejecutarán más middleware que pudiera haber con posterioridad en el código que pudieran afectar al mismo path. Si la función de callback no termina, para que se ejecuten los siguientes middleware que afectaran al mismo path y que estén codificados más adelante esta deberá terminar con la ejecución de la función next() que aparecerá como tercer parámetro en la definición de la misma. Vemos algún ejemplo:
const express = require('express'); const app = express(); app.use((req, res, next) => { console.log("en primer middleware."); // cualquier petición entrará next(); // y continuará en siguientes middleware }) app.use("/", (req, res, next) => { console.log("en primer middleware de /."); // cualquier petición para / entrará next(); // y continuará en siguientes middleware }); app.get("/", (req, res, next) => { console.log("en primer get /"); // cualquier petición para / entrará next(); // y continuará en siguientes middleware }); app.get("/", (req, res, next) => { console.log("en segundo get /"); // cualquier petición para / entrará res.send("Home"); // envía y termina }); app.get("/", (req, res, next) => { console.log("en tercer get /"); // nunca llegará aquí res.send("Home"); // envía y termina }); app.get("/about", (req, res, next) => { // cualquier petición para / about res.send("Prueba de routing"); // envía y termina }); app.get("/data", (req, res, next) => { console.log("get en /data");{ // cualquier petición para / data // ni termina ni hace next, queda en estado indeterminado. Timeout en cliente }) app.use((req, res, next) => { console.log("para cualquier path que no sea / ni /about ni /data en get"); next(); // continúa en siguiente middleware }); app.use((req, res, next) => { res.status = 404; res.send("recurso no encontrado"); // termina }); app.listen(8080, () => { console.log('servidor funcionando en puerto 8080'); })
Los comentarios en el código explican el funcionamiento.
(1)https://developer.mozilla.org/es/docs/Learn/Server-side/Express_Nodejs/Introduction