Hapi vs Express: comparaci贸n de marcos web de Node.js

    Es probable que ya hayas o铆do hablar de Hapi. Y es posible que se pregunte c贸mo se compara con el marco web Express en el desarrollo de Node.js. En este art铆culo, compararemos los frameworks cara a cara y exploraremos las diferencias en la experiencia del desarrollador.

    Similitudes y diferencias entre Hapi y Express

    Ambos Express y Hapi aspiran a ser altamente flexibles, simples y extensibles. Esta similitud significa que ambos tienen API f谩ciles de usar, son altamente modulares y pueden admitir su aplicaci贸n a medida que crece potencialmente mucho.

    La curva de aprendizaje de estos marcos, dado que son bastante sencillos, es baja, a diferencia de un marco m谩s obstinado como Meteor. Si viene de usar Express, deber铆a poder recoger r谩pidamente a Hapi, y viceversa.

    Sin embargo, existen algunas diferencias filos贸ficas entre los dos marcos, que describiremos a lo largo de este art铆culo.

    Hapi.js incluye m谩s “bater铆as” por defecto que Express. Por ejemplo, al analizar la carga 煤til de formularios a trav茅s de solicitudes “POST”, con Express, debe incluir el body-parser middleware. Luego lo usa para analizar la carga 煤til POST y usar los datos del usuario. Por otro lado, con Hapi, no necesita middleware. La carga 煤til es analizada por usted por el propio marco, y puede acceder a la carga 煤til directamente en el objeto de solicitud. Peque帽as comodidades como esa abundan en Hapi.

    Enrutamiento y servidor b谩sico

    Para empezar crearemos hapiapp, una aplicaci贸n de ejemplo de Hapi que muestra la funcionalidad b谩sica de Hapi. Tambi茅n crearemos expressapp, una imagen especular de hapiapp que usa Express para que podamos comparar los marcos uno al lado del otro.

    Nuestras dos aplicaciones utilizar谩n ES6 JavaScript.

    Cree dos directorios, hapiapp y expressapp.

    Dentro de cada uno, ejecute el comando:

    $ npm init
    

    Luego acepte todos los valores predeterminados presionando “Enter”. Esto crea un archivo package.json dentro de cada directorio, creando as铆 nuestras dos aplicaciones diferentes, hapiapp y expressapp.

    Primero, veamos el enrutamiento b谩sico en acci贸n en Hapi.js. Dentro del directorio hapiapp, instale el m贸dulo Hapi ejecutando el siguiente comando:

    $ npm install [email聽protected] --save
    

    Luego crea un archivo index.js con el siguiente contenido:

    // hapiapp/index.js
    
    const Hapi = require('hapi');
    
    // create our server
    const server = new Hapi.Server();
    
    // configure the port
    server.connection({
        port: 8000,
        host: 'localhost'
    });
    
    // basic routes
    server.route({
        method: 'GET',
        path: "https://Pharos.sh.com/",
        handler: (request, reply) => reply('Hello World')
    });
    
    server.route({
        method: 'GET',
        path: '/hello/{name}',
        handler: (request, reply) => reply(`Hello ${request.params.name}`)
    });
    
    // start the server
    server.start((err) => {
        if (err) {
            console.error(err);
        }
    
        console.log('App running on port 8000...');
    });
    

    Esta aplicaci贸n b谩sica crea dos rutas en localhost:8000 y localhost:8000/hello/<name>. El primero imprimir谩 un simple “Hola mundo” para el usuario. La segunda, sin embargo, es una ruta din谩mica que imprime “Hola” seguido del nombre que ingresamos en el navegador despu茅s de la segunda barra “/”.

    Desde el directorio hapiapp, ejecute la aplicaci贸n usando el siguiente comando y pruebe las URL dadas anteriormente:

    $ node index.js
    

    La aplicaci贸n Express equivalente implica una configuraci贸n muy similar. Dentro de nuestro directorio expressapp, instale Express como una dependencia usando el comando:

    $ npm install [email聽protected] --save
    

    Luego crea un archivo index.js con el siguiente contenido:

    // expressapp/index.js
    
    const express = require('express');
    
    // create our app
    const app = express();
    
    // basic routes
    app.get("https://Pharos.sh.com/", (req, res) => res.send('Hello World!'));
    app.get('/hello/:name', (req, res) => res.send(`Hello ${req.params.name}`));
    
    // start the server
    app.listen(3000, () => console.log('App running on port 3000...'));
    

    Esto logra la misma funcionalidad que la aplicaci贸n Hapi. Para ejecutarlo, desde dentro de nuestro directorio expressapp, ejecute el comando:

    $ node index.js
    

    Visite las rutas indicadas anteriormente, excepto que ahora estamos corriendo en localhost:3000 en vez de localhost:8000.

    Ambos ejemplos de c贸digo tienen una simple simplicidad. La configuraci贸n del servidor es notablemente similar, aunque Express parece m谩s compacto aqu铆.

    Trabajando con Middleware

    “Middleware” es el nombre que se le da a los m贸dulos de software que funcionan en solicitudes HTTP en sucesi贸n antes de que el resultado final se devuelva al usuario en una respuesta.

    El middleware puede funcionar como funciones que definimos dentro de la aplicaci贸n, o funciones definidas en bibliotecas de middleware de terceros.

    Express le permite adjuntar middleware para manejar cada solicitud. Sin embargo, Hapi trabaja con plugins que brindan funcionalidad de middleware.

    Un ejemplo de middleware son los tokens CSRF, que ayudan a prevenir ataques de pirater铆a de la falsificaci贸n de solicitudes entre sitios (CSRF). Sin tokens CSRF, los atacantes pueden hacerse pasar por solicitudes leg铆timas y robar datos o ejecutar c贸digo malicioso en sus aplicaciones.

    Los enfoques adoptados por Hapi y Express para hacer frente a los ataques CSRF son similares. Implica generar un token secreto en el servidor para cada formulario que env铆a datos al servidor. Luego, el servidor verifica cada solicitud POST para el token correcto y dif铆cil de falsificar. Si est谩 ausente, el servidor rechaza la solicitud, evitando as铆 intentos de ejecuci贸n de c贸digo malicioso. En este caso, el middleware generar谩 un token CSRF para cada solicitud para que no tenga que hacerlo dentro del c贸digo de su aplicaci贸n.

    La diferencia entre los dos marcos aqu铆 es principalmente cosm茅tica, con Hapi usando un plugin, Crumb, para la generaci贸n y procesamiento de tokens. Express, por otro lado, utiliza un middleware conocido como csurf para generar y procesar tokens CSRF.

    Aqu铆 hay un ejemplo de c贸digo Hapi que genera tokens Crumb:

    'use strict';
    
    const Hapi = require('hapi');
    const Vision = require('vision');
    
    const server = new Hapi.Server({
        host: '127.0.0.1',
        port: 8000
    });
    
    const plugins = [
        Vision,
        {
            plugin: require('../'),
            options: {
                restful: true
            }
        }
    ];
    
    // Add Crumb plugin
    (async () => {
        await server.register(plugins);
    
        server.route([
            // a "crumb" cookie should be set with any request
            // for cross-origin requests, set CORS "credentials" to true
            // a route returning the crumb can be created like this
    
            {
                method: 'GET',
                path: '/generate',
                handler: function (request, h) {
    
                    return {
                        crumb: server.plugins.crumb.generate(request, h)
                    };
                }
            },
    
            // request header "X-CSRF-Token" with crumb value must be set in request for this route
    
            {
                method: 'PUT',
                path: '/crumbed',
                handler: function (request, h) {
    
                    return 'Crumb route';
                }
            }
        ]);
    
        await server.start();
    
        console.log('Example restful server running at:', server.info.uri);
    })();
    

    Y el c贸digo Express aproximadamente equivalente usando csurf.

    const cookieParser = require('cookie-parser')
    const csrf = require('csurf')
    const bodyParser = require('body-parser')
    const express = require('express')
    
    // setup route middlewares
    let csrfProtection = csrf({ cookie: true })
    let parseForm = bodyParser.urlencoded({ extended: false })
    
    // create express app
    let app = express()
    
    // parse cookies
    // we need this because "cookie" is true in csrfProtection
    app.use(cookieParser())
    
    app.get('/form', csrfProtection, function (req, res) {
        // pass the csrfToken to the view
        res.render('send', { csrfToken: req.csrfToken() })
    });
    
    app.post('/process', parseForm, csrfProtection, function (req, res) {
        res.send('data is being processed')
    });
    

    Las bibliotecas de middleware tambi茅n pueden ayudarlo con la implementaci贸n de otras funciones comunes, como la autenticaci贸n. Puede encontrar una lista de algunos de los middleware Express en este directorio de middleware. Para Hapi, hay un biblioteca de plugins de Hapi.

    Entrega de im谩genes y activos est谩ticos

    El servicio de archivos est谩ticos es muy similar entre Hapi y Express. Hapi usa el Inerte plugin, mientras que Express utiliza el express.static middleware incorporado.

    En nuestra aplicaci贸n hapiapp, podemos servir archivos est谩ticos desde un directorio p煤blico creando un directorio en hapiapp/public y la colocaci贸n en 茅l de un archivo runsagainstbulls.jpg.

    Aqu铆 est谩 el archivo de imagen. Haga clic derecho y desc谩rguelo en su aplicaci贸n.

    Tambi茅n querr谩s instalar [email聽protected] utilizando:

    $ npm install [email聽protected] --save
    

    Ahora podemos servir el archivo est谩tico usando el siguiente c贸digo. Agregue esto antes de las l铆neas que inician el servidor en hapiapp/index.js.

    // hapiapp/index.js
    
    ...
    
    // serve static files using inert
    // require inert for this route
    server.register(require('inert'), (err) => {
        if (err) {
            throw err;
        }
    
        // serve the runswithbulls image from the public folder
        server.route({
            method: 'GET',
            path: '/image',
            handler: (request, reply) => {
                reply.file('./public/runsagainstbulls.jpg');
            }
        });
    });
    

    Ejecute el servidor nuevamente y visite localhost:8000/image y deber铆a ver la imagen que se le devuelve.

    En Express, hay un poco menos de configuraci贸n. Simplemente copie la misma imagen en una carpeta p煤blica reci茅n creada dentro de expressapp. Luego agregue esta l铆nea a su archivo index.js antes de la l铆nea que inicia el servidor:

    // expressapp/index.js
    
    ...
    
    // serve static files from the public folder
    app.use(express.static('public'));
    

    Inicie su servidor y visite localhost:3000/runsagainstbulls.jpg y deber铆a ver la imagen que ofrece Express.

    De forma predeterminada, Express servir谩 todos los archivos en la carpeta configurada y Hapi se puede configurar para que haga lo mismo con Inert, aunque no es la configuraci贸n predeterminada.

    Usar motores de plantilla

    Tanto Hapi como Express pueden renderizar y servir plantillas como usar motores como Handlebars, Twig, EJS y otros.

    En nuestra carpeta hapiapp, instale el plugin de visi贸n para brindar soporte con:

    $ npm install [email聽protected] --save
    

    Tambi茅n instalar Bigote daliniano motor de plantillas con:

    $ npm install [email聽protected] --save
    

    Luego, creemos una plantilla de Handlebars en un nuevo directorio de plantillas y un nuevo archivo en ese directorio, llamado cats.html. Vamos a hacer una lista de gatos y mostrarlos usando esta plantilla.

    Actualice cats.html de la siguiente manera:

    <!-- hapiapp/templates/cats.html -->
    <h2>All My Cats</h2>
    
    <ol>
        {{#each cats}}
            <li>{{name}}</li>
        {{/each}}
    </ol>
    

    Esta plantilla de Handlebars recorre una colecci贸n de objetos de gatos y muestra el nombre de cada gato.

    Dentro de index.js, antes de iniciar el servidor, agregue el siguiente c贸digo, que configura la visi贸n y crea una ruta en /catsy proporciona una lista de gatos que se mostrar谩n all铆.

    // hapiapp/index.js
    
    ...
    
    // configure vision to render Handlebars templates
    server.register(require('vision'), (err) => {
        if (err) {
            throw err;
        }
    
        server.views({
            engines: {
                html: require('handlebars'),
            },
            path: __dirname + '/templates'
        });
    });
    
    // display cats at /cats using the cats handlebars template
    server.route({
        method: 'GET',
        path: '/cats',
        handler: (request, reply) => {
    
            reply.view('cats', {
                cats: [
                    {name: "Blinky the Cat"},
                    {name: "Sammy the Happy Cat"},
                    {name: "Eto the Thug Cat"},
                    {name: "Liz a quiet cat"},
                ]
            });
        }
    });
    

    Inicie el servidor nuevamente y visite localhost:8000/cats, y deber铆a ver una lista de gatos en la p谩gina.

    Para replicar esta funcionalidad en Express, primero cree un directorio llamado vistas dentro del directorio ra铆z de expressapp. Luego crea un archivo de Handlebars cats.hbs con contenido similar al que ten铆amos para Hapi.

    <!-- expressapp/views/cats.hbs -->
    <h2>All My Cats</h2>
    
    <ol>
        {{#each cats}}
            <li>{{name}}</li>
        {{/each}}
    </ol>
    

    Ahora, de vuelta en expressapp, instale el motor Handlebars para Express con:

    $ npm install [email聽protected] --save
    

    Luego, para renderizar la plantilla y mostrar nuestros gatos, actualice index.js, agregando el siguiente c贸digo justo antes de la parte que inicia el servidor:

    // expressapp/index.js
    
    ...
    
    // use handlebars as the view engine
    app.set('view engine', 'hbs');
    
    // display a list of cats at /cats
    app.get('/cats', function (req, res) {
    
        res.render('cats', {
            cats: [
                {name: "Blinky the Cat"},
                {name: "Sammy the Happy Cat"},
                {name: "Eto the Thug Cat"},
                {name: "Liz a quiet cat"},
            ]
        });
    });
    

    Reinicie el servidor Express y visite localhost:3000/cats, y deber铆amos ver la lista de gatos mostrada como estaba para Hapi.

    Notar谩 que el ejemplo de Hapi necesitaba un poco m谩s de configuraci贸n que el ejemplo Express. Sin embargo, ambos ejemplos de c贸digo son bastante sencillos de seguir. En una aplicaci贸n peque帽a como esta, hay poco para elegir entre los dos marcos. En una aplicaci贸n m谩s grande, sin embargo, habr谩 diferencias m谩s notables. Por ejemplo, Hapi tiene la reputaci贸n de incluir m谩s c贸digo repetitivo que Express. Por otro lado, tambi茅n hace un poco m谩s por usted que Express, por lo que definitivamente hay compensaciones.

    Conectarse a una base de datos

    MongoDB es una base de datos NoSQL probada en batalla para usar en sus aplicaciones. Con el mangosta biblioteca de modelado de objetos, podemos conectar MongoDB a las aplicaciones Hapi y Express. Para esta parte, querr谩 tener MongoDB instalado y ejecut谩ndose en su sistema si a煤n no lo tiene.

    Para ver c贸mo funciona esto para ambos marcos, actualicemos nuestras aplicaciones de muestra para almacenar objetos cat en una base de datos. Asimismo, nuestros listados buscar谩n objetos cat de la base de datos.

    Los pasos que tenemos que seguir son:

    • Configurar una conexi贸n de MongoDB a localhost
    • Definir un esquema de mangosta
    • Crea objetos
    • Almacenar objetos
    • Recuperar documentos de la base de datos

    De vuelta en nuestro directorio hapiapp, instale mangosta con:

    $ npm install [email聽protected] --save
    

    Dado que queremos la capacidad de crear nuevos gatos a partir de nuestro /cats ver, actualice la plantilla de gatos para incluir un formulario para gatos nuevos. Agregue las siguientes l铆neas encima de la lista de gatos en hapiapp/templates/cats.html.

    <!-- hapiapp/templates/cats.html -->
    
    ...
    
    <form class="" action="/cats" method="post">
        <input type="text" name="name" placeholder="New cat">
    </form>
    

    Ahora podemos importar mangosta en la parte superior de nuestro archivo index.js. Justo debajo de la l铆nea donde importamos Hapi, agregue las siguientes l铆neas:

    // hapiapp/index.js
    
    ...
    
    const Hapi = require('hapi');
    const mongoose = require('mongoose');
    
    // connect to MongoDB
    mongoose.connect('mongodb://localhost/hapiappdb', { useMongoClient: true })
        .then(() => console.log('MongoDB connected'))
        .catch(err => console.error(err));
    
    // create a Cat model that can be persisted to the database
    const Cat = mongoose.model('Cat', {name: String});
    

    Ahora actualice nuestra ruta “GET” para /cats para buscar gatos din谩micamente desde MongoDB. Tambi茅n agregaremos una nueva ruta “POST” donde podemos guardar gatos en la base de datos. Aqu铆 est谩 el c贸digo para realizar estos cambios, reemplazando el antiguo GET /cats c贸digo:

    // hapiapp/index.js
    
    ...
    
    // display cats at /cats using the cats handlebars template
    server.route({
        method: 'GET',
        path: '/cats',
        handler: (request, reply) => {
    
            let cats = Cat.find((err, cats) => {
                console.log(cats);
                reply.view('cats', {cats: cats});
            });
        }
    });
    
    // post cats
    server.route({
        method: 'POST',
        path: '/cats',
        handler: (request, reply) => {
            let catname = request.payload.name;
            let newCat = new Cat({name: catname});
    
            newCat.save((err, cat) => {
                if (err) {
                    console.error(err);
                }
    
                return reply.redirect().location('cats');
            });
        }
    });
    

    Ahora, cuando vuelva a ejecutar el servidor y visite localhost:8000/cats, ver谩 que la lista de gatos ahora est谩 vac铆a. Pero hay un formulario donde puede ingresar nuevos nombres de gatos y presionar Entrar key, y sus gatos se guardar谩n en MongoDB. Ingrese algunos gatos y obtendr谩 una lista de gatos, como se muestra a continuaci贸n:

    Para Express, la instalaci贸n y configuraci贸n de Mongoose se ver谩 casi igual. De vuelta en expressapp, instale Mongoose con:

    $ npm install [email聽protected] --save
    

    Luego agregue el c贸digo de conexi贸n de Mongoose y Cat modelo en index.js despu茅s de importar Express.

    // expressapp/index.js
    
    ...
    
    const express = require('express');
    const mongoose = require('mongoose');
    
    // connect to MongoDB
    mongoose.connect('mongodb://localhost/expressappdb', { useMongoClient: true })
        .then(() => console.log('MongoDB connected'))
        .catch(err => console.error(err));
    
    // create a Cat model that can be persisted to the database
    const Cat = mongoose.model('Cat', {name: String});
    

    Note la similitud con lo que hicimos por Hapi.

    Ahora actualice nuestra plantilla expressapp / views / cats.hbs con el c贸digo del formulario.

    <!-- expressapp/views/cats.hbs -->
    
    ...
    
    <form class="" action="/cats" method="post">
        <input type="text" name="name" placeholder="New cat">
    </form>
    

    Para permitir que Express analice el contenido del formulario, necesitaremos importar el middleware body-parser. Despu茅s de la l铆nea de importaci贸n Express en index.js, importe body-parser de la siguiente manera:

    // expressapp/index.js
    
    ...
    
    const express = require('express');
    const bodyParser = require('body-parser')
    

    Adem谩s, active el middleware despu茅s de la l铆nea creando nuestra aplicaci贸n Express, de la siguiente manera:

    // expressapp/index.js
    
    ...
    
    // create our app
    const app = express();
    
    // parse application/x-www-form-urlencoded
    app.use(bodyParser.urlencoded({ extended: false }));
    

    Ahora actualice nuestra ruta “GET” para /cats y cree una nueva ruta “POST” que cree gatos en la base de datos:

    // expressapp/index.js
    
    ...
    
    // display a list of cats at /cats
    app.get('/cats', function (req, res) {
    
        let cats = Cat.find((err, cats) => {
            console.log(cats);
            res.render('cats', {cats: cats});
        });
    });
    
    // post new cats and save them in the database
    app.post('/cats', function (req, res) {
        console.log(req.body.name);
    
        let catname = req.body.name;
        let newCat = new Cat({name: catname});
    
        newCat.save((err, cat) => {
            if (err) {
                console.error(err);
            }
            console.log(`Cat ${req.body.name} saved to database.`)
            res.redirect('/cats');
        });
    });
    

    Ahora, reinicie el servidor y visite localhost:3000/cats. Al igual que para la aplicaci贸n Hapi, ahora veremos un espacio vac铆o con un formulario en la parte superior. Podemos ingresar nombres de gatos. La vista se actualizar谩 y nos mostrar谩 una lista de gatos ahora guardados en nuestra base de datos MongoDB.

    Este es un caso en el que la configuraci贸n Express fue un poco m谩s compleja que el equivalente de Hapi. Sin embargo, tenga en cuenta que en realidad no tuvimos que hacer demasiado con Hapi o Express para configurar la conexi贸n de la base de datos; la mayor parte del trabajo aqu铆 fue para las rutas GET y POST.

    Elegir entre Hapi y Express

    Como vimos, Hapi y Express tienen mucho en com煤n. Ambos tienen una base de usuarios activa, y Hapi est谩 siendo ampliamente adoptado por equipos de grandes empresas. Sin embargo, Express sigue superando a la mayor铆a de los otros marcos en adopci贸n, especialmente para equipos m谩s peque帽os.

    Hapi tiene sentido si tiene una idea de aplicaci贸n bien definida que se ajuste a valores predeterminados sensibles para el enfoque de programaci贸n. Hapi adopta un enfoque particular para cosas como la seguridad de las aplicaciones, y su sistema de plugins no es tan extenso como el ecosistema de middleware Express. Si es un equipo grande, las convenciones de Hapi pueden resultar 煤tiles para mantener su c贸digo en condiciones de mantenimiento.

    Express, por otro lado, funciona mejor si busca flexibilidad y toma la mayor铆a de las decisiones de desarrollo por su cuenta. Por ejemplo, puede haber diez middleware diferentes que hagan lo mismo en el ecosistema Express. Depende de usted considerar entre muchas opciones y hacer su elecci贸n. Express es mejor si tiene una aplicaci贸n y no est谩 seguro de la direcci贸n exacta que tomar谩. Independientemente de las contingencias que surjan, el gran ecosistema Express proporcionar谩 las herramientas para que esto suceda.

    Aprende m谩s

    Puede obtener un gran comienzo visitando los sitios web respectivos de cada marco:

    De cualquier manera, lo m谩s importante es que realmente los pruebe, trabaje con algunos ejemplos y decida por su cuenta cu谩l es el mejor para usted.

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *