Creazione di un modello generico in Node.js con MySQL

L'adozione di un modello generico in un'applicazione Node.js può semplificare notevolmente la gestione delle operazioni CRUD (Create, Read, Update, Delete) su un database MySQL. In questo articolo, esploreremo come creare da zero un modello generico utilizzando il package mysql2, uno strumento popolare per interfacciarsi con MySQL in Node.js.

Prima di iniziare, assicurati di avere Node.js installato sul tuo sistema. Successivamente, avrai bisogno del package mysql2, che puoi installare utilizzando npm:


npm install mysql2

Il primo passo è configurare la connessione al database MySQL. Creiamo un file db.js per gestire questa connessione:


// db.js
const mysql = require('mysql2');

const connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'your_password',
    database: 'your_database'
});

connection.connect((err) => {
    if (err) {
        console.error('Error connecting to the database:', err.stack);
        return;
    }
    console.log('Connected to the database');
});

module.exports = connection;

In questo file, configuriamo i dettagli della connessione come l'host, l'utente, la password e il nome del database. Il metodo connect gestisce la connessione e restituisce un errore se qualcosa va storto.

Ora possiamo creare un modello generico che includa metodi per le operazioni CRUD. Creiamo un file GenericModel.js:


// GenericModel.js
const db = require('./db');

class GenericModel {
    constructor(tableName) {
        this.tableName = tableName;
    }

    findAll() {
        const query = `SELECT * FROM ${this.tableName}`;
        return new Promise((resolve, reject) => {
            db.query(query, (err, results) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(results);
                }
            });
        });
    }

    findById(id) {
        const query = `SELECT * FROM ${this.tableName} WHERE id = ${id}`;
        return new Promise((resolve, reject) => {
            db.query(query, (err, results) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(results[0]);
                }
            });
        });
    }

    create(data) {
        const columns = Object.keys(data).join(', ');
        const values = Object.values(data).map(value => `'${value}'`).join(', ');
        const query = `INSERT INTO ${this.tableName} (${columns}) VALUES (${values})`;
        return new Promise((resolve, reject) => {
            db.query(query, (err, results) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(results.insertId);
                }
            });
        });
    }

    update(id, data) {
        const updates = Object.entries(data).map(([key, value]) => `${key} = '${value}'`).join(', ');
        const query = `UPDATE ${this.tableName} SET ${updates} WHERE id = ${id}`;
        return new Promise((resolve, reject) => {
            db.query(query, (err, results) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(results.affectedRows);
                }
            });
        });
    }

    delete(id) {
        const query = `DELETE FROM ${this.tableName} WHERE id = ${id}`;
        return new Promise((resolve, reject) => {
            db.query(query, (err, results) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(results.affectedRows);
                }
            });
        });
    }
}

module.exports = GenericModel;

Questo modello generico utilizza la classe GenericModel per eseguire operazioni di base come findAll, findById, create, update e delete. Ogni metodo genera una query SQL concatenata e la esegue utilizzando db.query.

Una volta creato il modello generico, possiamo creare modelli specifici per ogni tabella nel database. Ad esempio, se abbiamo una tabella users, possiamo creare un file UserModel.js:


// UserModel.js
const GenericModel = require('./GenericModel');

class UserModel extends GenericModel {
    constructor() {
        super('users'); // Passa il nome della tabella al costruttore del modello generico
    }

    // Puoi aggiungere metodi specifici per il modello User se necessario
}

module.exports = new UserModel();

Qui, UserModel estende GenericModel, passando il nome della tabella users al costruttore della classe madre. Questo ci permette di ereditare tutti i metodi CRUD senza dover riscrivere il codice.

Uno dei problemi principali nell'utilizzo della concatenazione delle stringhe per costruire query SQL è la vulnerabilità alle iniezioni SQL. Per esempio, se un utente malevolo inserisce un valore malformato come input, potrebbe compromettere il database. Una pratica migliore è utilizzare query parametrizzate, come illustrato di seguito per il metodo findById:


findById(id) {
    const query = `SELECT * FROM ${this.tableName} WHERE id = ?`;
    return new Promise((resolve, reject) => {
        db.query(query, [id], (err, results) => {
            if (err) {
                reject(err);
            } else {
                resolve(results[0]);
            }
        });
    });
}

In questo esempio, il valore dell'ID viene passato come parametro separato, prevenendo il rischio di iniezione SQL. Utilizzare query parametrizzate è altamente raccomandato in qualsiasi applicazione destinata alla produzione.

Conclusione

In questo articolo, abbiamo esplorato come creare un modello generico in Node.js utilizzando il package mysql2 per interagire con un database MySQL. Abbiamo visto come implementare metodi CRUD di base e come estendere questo modello generico per creare modelli specifici per tabelle diverse. Infine, abbiamo discusso l'importanza della sicurezza e delle query parametrizzate per prevenire vulnerabilità comuni come l'iniezione SQL.

Questo approccio modulare e riutilizzabile può rendere il tuo codice più pulito, più facile da mantenere e più sicuro, consentendoti di concentrarti maggiormente sulla logica della tua applicazione anziché sulle complessità dell'interazione con il database.

Torna su