Lab 17: Interacción con la base de datos

Descripción

En esta actividad comenzaremos con la interacción con una base de datos desde node.

Modalidad

Individual.

Objetivos de aprendizaje
  • Entender cómo interactúa una aplicación web con una base de datos.
  • Desarrollar aplicaciones web que interactúen con bases de datos.
Instrucciones
  • Para interactuar con una base de datos, debemos crear la base de datos. Para este laboratorio usaremos MySQL, sin embargo, la lógica es muy similar si decides trabajar con cualquier otro motor de bases de datos. Crea tu base de datos y crea algunas tablas y ponles algunos datos, de manera similar a la demostración del profesor.
  • Para poder interactuar con el manejador de base de datos MySQL, ocuparemos el paquete mysql2.
  • Para poder conectarnos con la base de datos, utilizaremos el archivo database.js, el cual crearemos dentro de un folder con nuestras librerías de apoyo, típicamente nombrado util. El archivo se encargará de manejar las conexiones con nuestra base de datos:
    const mysql = require('mysql2');
    
    const pool = mysql.createPool({
        host: 'localhost',
        user: 'root',
        database: 'database_name',
        password: 'el_password_de_tu_usuario_de_la_bd'
    });
    
    module.exports = pool.promise();
                        
    Asegúrate de cambiar los valores de los atributos del objeto de js para que coincidan con los de tu base de datos. Como podrás observar, se exporta una promesa. Las promesas permiten manejar fácilmente código que se ejecuta de manera asíncrona.
  • Para conectarnos con la base de datos y ejecutar consultas desde nuestra aplicación:
    const db = require('./util/database');
    
    db.execute('Consulta SQL por ejemplo: SELECT * FROM mi_tabla');
                        
    Ahora, debido a que en database.js devolvimos una promesa, esto nos permite hacer algo después de que ejecutamos la consulta con el método .then(), e incluso manejar los errores con el método .catch(). Por ejemplo, si queremos recuperar los registros de la tabla mascotas:
    db.execute('SELECT * FROM mascotas')
        .then(([rows, fieldData]) => {
            console.log(rows);
        })
        .catch(err => {
            console.log(err);
        });
                        
    En la varibale rows, tendremos cada uno de de los registros de nuestra consulta.
  • El código de interacción con la base de datos, si seguimos buenas prácticas, lo escribiremos siempre en nuestros modelos. Por lo que normalmente, el método fetchAll() de nuestros modelos quedaría con el siguiente formato:
    static fetchAll() {
        return db.execute('SELECT * FROM mascotas');
    }
                    
    Y el código del controlador (asumiendo que tenemos un template de la vista llamado 'vista.html' que despliega el contenido de un arreglo de js llamado mascotas):
    exports.getMascotas = (request, response, next) => {
        Mascota.fetchAll()
            .then(([rows, fieldData]) => {
                response.render('vista', {
                    mascotas: rows
                })
            })
            .catch(err => console.log(err));
    }
                    
  • Ahora, para insertar un registro en la base de datos, nuestro código del método save() en los modelos, tendría el siguiente formato:
    save() {
        return db.execute('INSERT INTO mascotas (nombre_columna_1, nombre_columna_2) VALUES (?, ?)',
            [this.variable_valor_1, this.variable_valor_2]
        );
    
    }
                        
    Como podrás ver, no se insertan los valores directamente en el string, sino se pone un signo de interrogación, esto es para evitar ataques de inyección de SQL, ya que el método execute, al pasar estos datos en un arreglo como segundo argumento, evita que si se inserta código SQL, éste no se ejecute y simplemente sea interpretado como un string.
    El código del controlador quedaría con el siguiente formato:
    exports.insertarMascota = (request, response, next) => {
        const mascota = new Mascota(request.body.nombre, request.body.descripcion);
        mascota.save().then(() => {
            response.redirect('/');
        }).catch(err => console.log(err));
    };
                        
  • En ocasiones es necesario recuperar un registro en particular de la base de datos, y muchas veces queremos que esto pueda realizarse desde las rutas. Para indicarle al ruteador de express que un valor en una ruta es una varibale, podemos hacerlo agregando como prefijo el símbolo : seguido del nombre que le queremos dar a la variable:
    router.get('/mascotas/:mascota_id', controllerMascotas.getMascota);
                        
    Y en el controlador para hacer uso de la variable:
    export.getMascota = (request, response, next) => {
        const id = request.params.mascota_id;
        //Resto del código del controlador...
    }
                        
  • Continúa mejorando tus laboratorios anteriores o tu proyecto agregándoles interacción con la base de datos. Asegúrate de al menos realizar una consulta que devuelva varios registros, una consulta que devuelva 1 sólo registro, una inserción, y una edición de un registro de la base de datos. Recuerda que siempre tienes también la opción de crear una nueva aplicación.
Preguntas a responder
  • ¿Qué ventajas tiene escribir el código SQL únicamente en la capa del modelo?
  • ¿Qué es SQL injection y cómo se puede prevenir?
Especificaciones de entrega

A través de tu repositorio personal (Bitbucket o GitHub).