Node
Tutorial Node: Módulo mysql2
Aprende a usar mysql2 en Node.js para conectar, consultar y realizar inserciones seguras en bases de datos MySQL con ejemplos prácticos y promesas.
Aprende Node y certifícateConexión a base de datos MySQL
La conexión a bases de datos es una funcionalidad esencial en aplicaciones Node.js que requieren persistencia de datos. MySQL, siendo uno de los sistemas de gestión de bases de datos más utilizados, cuenta con excelente soporte en el ecosistema de Node.js a través del módulo mysql2.
El módulo mysql2 es una biblioteca nativa que proporciona una interfaz completa para interactuar con bases de datos MySQL desde Node.js. A diferencia de su predecesor mysql, mysql2 ofrece mejor rendimiento, soporte para promesas nativas y compatibilidad con las últimas características de MySQL.
Instalación del módulo mysql2
Para comenzar a trabajar con MySQL en Node.js, necesitas instalar el módulo mysql2 utilizando npm:
npm install mysql2
Una vez instalado, puedes importar el módulo en tu aplicación Node.js y comenzar a establecer conexiones con tu base de datos MySQL.
Configuración básica de conexión
La configuración de conexión requiere especificar los parámetros necesarios para establecer comunicación con el servidor MySQL. Estos parámetros incluyen el host, puerto, usuario, contraseña y nombre de la base de datos:
const mysql = require('mysql2');
// Configuración de conexión
const connectionConfig = {
host: 'localhost',
port: 3306,
user: 'tu_usuario',
password: 'tu_contraseña',
database: 'nombre_base_datos'
};
Establecimiento de conexión simple
Para crear una conexión individual, utiliza el método createConnection()
que acepta la configuración como parámetro:
const mysql = require('mysql2');
// Crear conexión
const connection = mysql.createConnection({
host: 'localhost',
port: 3306,
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
// Establecer conexión
connection.connect((error) => {
if (error) {
console.error('Error conectando a MySQL:', error.message);
return;
}
console.log('Conectado a MySQL con ID:', connection.threadId);
});
Manejo de errores en conexiones
El manejo de errores es fundamental para crear aplicaciones robustas. Los errores más comunes incluyen credenciales incorrectas, servidor no disponible o base de datos inexistente:
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'usuario_incorrecto',
password: 'contraseña_incorrecta',
database: 'base_datos_inexistente'
});
connection.connect((error) => {
if (error) {
switch (error.code) {
case 'ER_ACCESS_DENIED_ERROR':
console.error('Error: Credenciales de acceso incorrectas');
break;
case 'ECONNREFUSED':
console.error('Error: No se puede conectar al servidor MySQL');
break;
case 'ER_BAD_DB_ERROR':
console.error('Error: La base de datos no existe');
break;
default:
console.error('Error de conexión:', error.message);
}
return;
}
console.log('Conexión establecida correctamente');
});
Pool de conexiones
Para aplicaciones que requieren múltiples conexiones simultáneas, el uso de un pool de conexiones es más eficiente que crear conexiones individuales:
const mysql = require('mysql2');
// Crear pool de conexiones
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online',
connectionLimit: 10, // Máximo 10 conexiones simultáneas
queueLimit: 0, // Sin límite en la cola de espera
acquireTimeout: 60000, // Timeout de 60 segundos
timeout: 60000, // Timeout de consulta de 60 segundos
reconnect: true // Reconexión automática
});
// Obtener conexión del pool
pool.getConnection((error, connection) => {
if (error) {
console.error('Error obteniendo conexión del pool:', error.message);
return;
}
console.log('Conexión obtenida del pool, ID:', connection.threadId);
// Liberar conexión de vuelta al pool
connection.release();
});
Uso de promesas con mysql2
El módulo mysql2 incluye soporte nativo para promesas, lo que permite escribir código más limpio utilizando async/await:
const mysql = require('mysql2/promise');
async function conectarBaseDatos() {
try {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
console.log('Conexión establecida con promesas');
// Cerrar conexión
await connection.end();
console.log('Conexión cerrada correctamente');
} catch (error) {
console.error('Error en la conexión:', error.message);
}
}
conectarBaseDatos();
Pool de conexiones con promesas
La combinación de pools y promesas proporciona la máxima eficiencia y legibilidad del código:
const mysql = require('mysql2/promise');
// Crear pool con soporte para promesas
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online',
connectionLimit: 10,
queueLimit: 0
});
async function obtenerConexion() {
try {
const connection = await pool.getConnection();
console.log('Conexión obtenida del pool con promesas');
// Realizar operaciones con la conexión
// ...
// Liberar conexión
connection.release();
} catch (error) {
console.error('Error con el pool de conexiones:', error.message);
}
}
obtenerConexion();
Configuración avanzada de conexión
Para entornos de producción, es recomendable utilizar configuraciones avanzadas que incluyan parámetros de seguridad y rendimiento:
const mysql = require('mysql2');
const advancedConfig = {
host: 'localhost',
port: 3306,
user: 'app_user',
password: process.env.DB_PASSWORD, // Usar variables de entorno
database: 'produccion_db',
charset: 'utf8mb4', // Soporte completo UTF-8
timezone: '+00:00', // UTC timezone
ssl: {
rejectUnauthorized: false // Para conexiones SSL
},
acquireTimeout: 60000,
timeout: 60000,
reconnect: true,
multipleStatements: false // Seguridad: evitar múltiples consultas
};
const connection = mysql.createConnection(advancedConfig);
Cierre de conexiones
Es importante cerrar las conexiones correctamente para liberar recursos del servidor MySQL:
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
// Realizar operaciones...
// Cerrar conexión individual
connection.end((error) => {
if (error) {
console.error('Error cerrando conexión:', error.message);
return;
}
console.log('Conexión cerrada correctamente');
});
// Para pools de conexiones
// pool.end(() => {
// console.log('Pool de conexiones cerrado');
// });
La gestión adecuada de conexiones es fundamental para el rendimiento y estabilidad de aplicaciones Node.js que interactúan con MySQL. El módulo mysql2 proporciona todas las herramientas necesarias para establecer conexiones seguras, eficientes y escalables.
Sentencias SELECT
Las consultas SELECT constituyen la base fundamental para recuperar información de bases de datos MySQL desde Node.js. Una vez establecida la conexión con mysql2, puedes ejecutar consultas SQL para obtener datos específicos de tus tablas de manera eficiente y controlada.
La sintaxis básica de una consulta SELECT en mysql2 utiliza el método query()
que acepta la sentencia SQL como primer parámetro y una función callback para manejar los resultados:
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
// Consulta SELECT básica
connection.query('SELECT * FROM productos', (error, results, fields) => {
if (error) {
console.error('Error en la consulta:', error.message);
return;
}
console.log('Productos encontrados:', results.length);
console.log(results);
});
Consultas con filtros WHERE
Las cláusulas WHERE permiten filtrar registros según criterios específicos. Es fundamental utilizar consultas parametrizadas para evitar inyecciones SQL:
// Consulta con parámetro único
const productoId = 5;
connection.query(
'SELECT nombre, precio, stock FROM productos WHERE id = ?',
[productoId],
(error, results) => {
if (error) {
console.error('Error:', error.message);
return;
}
if (results.length > 0) {
console.log('Producto encontrado:', results[0]);
} else {
console.log('Producto no encontrado');
}
}
);
Para múltiples parámetros, utiliza un array con los valores en el orden correspondiente a los marcadores de posición:
// Consulta con múltiples parámetros
const precioMinimo = 100;
const categoria = 'electronica';
connection.query(
'SELECT id, nombre, precio FROM productos WHERE precio >= ? AND categoria = ?',
[precioMinimo, categoria],
(error, results) => {
if (error) {
console.error('Error:', error.message);
return;
}
console.log(`Productos de ${categoria} con precio >= ${precioMinimo}:`);
results.forEach(producto => {
console.log(`- ${producto.nombre}: €${producto.precio}`);
});
}
);
Consultas con promesas
El uso de promesas simplifica el manejo de consultas asíncronas y permite utilizar async/await para código más legible:
const mysql = require('mysql2/promise');
async function obtenerProductos() {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
try {
// Consulta con promesas
const [rows, fields] = await connection.execute(
'SELECT id, nombre, precio FROM productos WHERE stock > ?',
[0]
);
console.log('Productos disponibles:', rows.length);
return rows;
} catch (error) {
console.error('Error en consulta:', error.message);
throw error;
} finally {
await connection.end();
}
}
// Uso de la función
obtenerProductos()
.then(productos => {
productos.forEach(p => console.log(`${p.nombre}: €${p.precio}`));
})
.catch(error => console.error('Error:', error.message));
Consultas con ORDER BY y LIMIT
Para ordenar resultados y limitar la cantidad de registros devueltos, utiliza las cláusulas ORDER BY y LIMIT:
async function obtenerProductosOrdenados() {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
try {
// Productos más caros primero, limitados a 5
const [productos] = await connection.execute(
'SELECT nombre, precio FROM productos ORDER BY precio DESC LIMIT ?',
[5]
);
console.log('Top 5 productos más caros:');
productos.forEach((producto, index) => {
console.log(`${index + 1}. ${producto.nombre}: €${producto.precio}`);
});
} catch (error) {
console.error('Error:', error.message);
} finally {
await connection.end();
}
}
Consultas con JOIN
Las consultas JOIN permiten combinar datos de múltiples tablas relacionadas:
async function obtenerProductosConCategoria() {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
try {
const [resultados] = await connection.execute(`
SELECT
p.id,
p.nombre as producto,
p.precio,
c.nombre as categoria
FROM productos p
INNER JOIN categorias c ON p.categoria_id = c.id
WHERE p.precio BETWEEN ? AND ?
ORDER BY p.precio ASC
`, [50, 200]);
console.log('Productos con categoría:');
resultados.forEach(item => {
console.log(`${item.producto} (${item.categoria}): €${item.precio}`);
});
} catch (error) {
console.error('Error en JOIN:', error.message);
} finally {
await connection.end();
}
}
Consultas con funciones agregadas
Las funciones agregadas como COUNT, SUM, AVG proporcionan información estadística sobre los datos:
async function obtenerEstadisticas() {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
try {
// Estadísticas generales
const [stats] = await connection.execute(`
SELECT
COUNT(*) as total_productos,
AVG(precio) as precio_promedio,
MIN(precio) as precio_minimo,
MAX(precio) as precio_maximo,
SUM(stock) as stock_total
FROM productos
`);
const estadisticas = stats[0];
console.log('Estadísticas de productos:');
console.log(`- Total: ${estadisticas.total_productos}`);
console.log(`- Precio promedio: €${estadisticas.precio_promedio.toFixed(2)}`);
console.log(`- Rango de precios: €${estadisticas.precio_minimo} - €${estadisticas.precio_maximo}`);
console.log(`- Stock total: ${estadisticas.stock_total}`);
} catch (error) {
console.error('Error en estadísticas:', error.message);
} finally {
await connection.end();
}
}
Manejo de resultados vacíos
Es importante validar los resultados de las consultas para manejar casos donde no se encuentren registros:
async function buscarProductoPorNombre(nombreBuscado) {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
try {
const [productos] = await connection.execute(
'SELECT * FROM productos WHERE nombre LIKE ?',
[`%${nombreBuscado}%`]
);
if (productos.length === 0) {
console.log(`No se encontraron productos que contengan: "${nombreBuscado}"`);
return null;
}
console.log(`Encontrados ${productos.length} productos:`);
productos.forEach(producto => {
console.log(`- ${producto.nombre}: €${producto.precio}`);
});
return productos;
} catch (error) {
console.error('Error en búsqueda:', error.message);
return null;
} finally {
await connection.end();
}
}
// Uso de la función
buscarProductoPorNombre('laptop')
.then(resultados => {
if (resultados) {
console.log('Búsqueda completada exitosamente');
}
});
Consultas con pool de conexiones
Para aplicaciones con múltiples consultas simultáneas, utiliza pools de conexiones para optimizar el rendimiento:
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online',
connectionLimit: 10
});
async function obtenerProductosPorCategoria(categoriaId) {
try {
const [productos] = await pool.execute(
'SELECT id, nombre, precio, stock FROM productos WHERE categoria_id = ?',
[categoriaId]
);
return productos;
} catch (error) {
console.error('Error obteniendo productos:', error.message);
throw error;
}
}
// Múltiples consultas simultáneas
async function procesarMultiplesConsultas() {
try {
const [categoria1, categoria2, categoria3] = await Promise.all([
obtenerProductosPorCategoria(1),
obtenerProductosPorCategoria(2),
obtenerProductosPorCategoria(3)
]);
console.log('Categoría 1:', categoria1.length, 'productos');
console.log('Categoría 2:', categoria2.length, 'productos');
console.log('Categoría 3:', categoria3.length, 'productos');
} catch (error) {
console.error('Error en consultas múltiples:', error.message);
}
}
Las consultas SELECT con mysql2 proporcionan flexibilidad completa para recuperar datos de MySQL desde Node.js. La combinación de consultas parametrizadas, promesas y pools de conexiones garantiza aplicaciones seguras, eficientes y escalables.
Sentencias INSERT
Las sentencias INSERT permiten agregar nuevos registros a las tablas de MySQL desde Node.js. Después de dominar las consultas SELECT, el siguiente paso natural es aprender a insertar datos de manera segura y eficiente utilizando el módulo mysql2.
La sintaxis básica de una sentencia INSERT utiliza el método query()
o execute()
con la estructura SQL INSERT INTO seguida del nombre de la tabla y los valores a insertar:
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
// INSERT básico con valores directos
connection.query(
'INSERT INTO productos (nombre, precio, stock, categoria_id) VALUES (?, ?, ?, ?)',
['Smartphone Samsung', 299.99, 15, 1],
(error, results) => {
if (error) {
console.error('Error insertando producto:', error.message);
return;
}
console.log('Producto insertado con ID:', results.insertId);
console.log('Filas afectadas:', results.affectedRows);
}
);
Inserción con promesas
El uso de promesas facilita el manejo de operaciones INSERT asíncronas y permite un mejor control de errores:
const mysql = require('mysql2/promise');
async function insertarProducto(nombre, precio, stock, categoriaId) {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
try {
const [result] = await connection.execute(
'INSERT INTO productos (nombre, precio, stock, categoria_id) VALUES (?, ?, ?, ?)',
[nombre, precio, stock, categoriaId]
);
console.log(`Producto "${nombre}" insertado con ID: ${result.insertId}`);
return result.insertId;
} catch (error) {
console.error('Error en inserción:', error.message);
throw error;
} finally {
await connection.end();
}
}
// Uso de la función
insertarProducto('Tablet iPad', 599.99, 8, 1)
.then(id => console.log('Inserción completada, ID:', id))
.catch(error => console.error('Falló la inserción:', error.message));
Inserción múltiple de registros
Para insertar múltiples registros de manera eficiente, utiliza una sola consulta INSERT con múltiples conjuntos de valores:
async function insertarMultiplesProductos(productos) {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
try {
// Preparar valores para inserción múltiple
const valores = productos.map(p => [p.nombre, p.precio, p.stock, p.categoria_id]);
const [result] = await connection.execute(
'INSERT INTO productos (nombre, precio, stock, categoria_id) VALUES ?',
[valores]
);
console.log(`${result.affectedRows} productos insertados`);
console.log('Primer ID insertado:', result.insertId);
return result;
} catch (error) {
console.error('Error en inserción múltiple:', error.message);
throw error;
} finally {
await connection.end();
}
}
// Datos de ejemplo
const nuevosProductos = [
{ nombre: 'Laptop Dell', precio: 899.99, stock: 5, categoria_id: 1 },
{ nombre: 'Mouse Logitech', precio: 29.99, stock: 50, categoria_id: 2 },
{ nombre: 'Teclado Mecánico', precio: 79.99, stock: 20, categoria_id: 2 }
];
insertarMultiplesProductos(nuevosProductos);
Validación de datos antes de insertar
Es fundamental validar los datos antes de realizar inserciones para mantener la integridad de la base de datos:
async function insertarProductoValidado(datosProducto) {
// Validaciones básicas
if (!datosProducto.nombre || datosProducto.nombre.trim().length === 0) {
throw new Error('El nombre del producto es obligatorio');
}
if (datosProducto.precio <= 0) {
throw new Error('El precio debe ser mayor que cero');
}
if (datosProducto.stock < 0) {
throw new Error('El stock no puede ser negativo');
}
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
try {
const [result] = await connection.execute(
'INSERT INTO productos (nombre, precio, stock, categoria_id, fecha_creacion) VALUES (?, ?, ?, ?, NOW())',
[
datosProducto.nombre.trim(),
parseFloat(datosProducto.precio),
parseInt(datosProducto.stock),
datosProducto.categoria_id
]
);
console.log('Producto validado e insertado:', result.insertId);
return result.insertId;
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
throw new Error('Ya existe un producto con ese nombre');
}
throw error;
} finally {
await connection.end();
}
}
// Ejemplo de uso con validación
const productoNuevo = {
nombre: 'Auriculares Sony',
precio: 149.99,
stock: 25,
categoria_id: 3
};
insertarProductoValidado(productoNuevo)
.then(id => console.log('Producto creado exitosamente'))
.catch(error => console.error('Error de validación:', error.message));
Inserción con campos opcionales
Para manejar campos opcionales en las inserciones, construye dinámicamente la consulta SQL según los datos disponibles:
async function insertarProductoFlexible(datos) {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
try {
// Construir campos y valores dinámicamente
const campos = ['nombre', 'precio'];
const valores = [datos.nombre, datos.precio];
const placeholders = ['?', '?'];
// Agregar campos opcionales si están presentes
if (datos.descripcion) {
campos.push('descripcion');
valores.push(datos.descripcion);
placeholders.push('?');
}
if (datos.stock !== undefined) {
campos.push('stock');
valores.push(datos.stock);
placeholders.push('?');
}
if (datos.categoria_id) {
campos.push('categoria_id');
valores.push(datos.categoria_id);
placeholders.push('?');
}
// Construir consulta SQL
const sql = `INSERT INTO productos (${campos.join(', ')}) VALUES (${placeholders.join(', ')})`;
const [result] = await connection.execute(sql, valores);
console.log('Producto insertado con campos flexibles:', result.insertId);
return result.insertId;
} catch (error) {
console.error('Error en inserción flexible:', error.message);
throw error;
} finally {
await connection.end();
}
}
// Ejemplo con diferentes combinaciones de campos
insertarProductoFlexible({
nombre: 'Cámara Canon',
precio: 799.99,
descripcion: 'Cámara profesional DSLR',
stock: 3
});
Inserción con pool de conexiones
Para aplicaciones con múltiples inserciones simultáneas, utiliza pools de conexiones para optimizar el rendimiento:
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online',
connectionLimit: 10
});
async function insertarProductoConPool(nombre, precio, stock, categoriaId) {
try {
const [result] = await pool.execute(
'INSERT INTO productos (nombre, precio, stock, categoria_id) VALUES (?, ?, ?, ?)',
[nombre, precio, stock, categoriaId]
);
return result.insertId;
} catch (error) {
console.error('Error insertando con pool:', error.message);
throw error;
}
}
// Procesar múltiples inserciones concurrentes
async function procesarLoteProductos(productos) {
try {
const promesasInsercion = productos.map(producto =>
insertarProductoConPool(
producto.nombre,
producto.precio,
producto.stock,
producto.categoria_id
)
);
const idsInsertados = await Promise.all(promesasInsercion);
console.log('Todos los productos insertados:', idsInsertados);
return idsInsertados;
} catch (error) {
console.error('Error en lote de inserciones:', error.message);
throw error;
}
}
Manejo de errores específicos en INSERT
Las inserciones pueden fallar por diversas razones, y es importante manejar cada tipo de error apropiadamente:
async function insertarConManejoErrores(datosProducto) {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mi_contraseña',
database: 'tienda_online'
});
try {
const [result] = await connection.execute(
'INSERT INTO productos (nombre, precio, stock, categoria_id) VALUES (?, ?, ?, ?)',
[datosProducto.nombre, datosProducto.precio, datosProducto.stock, datosProducto.categoria_id]
);
return {
exito: true,
id: result.insertId,
mensaje: 'Producto insertado correctamente'
};
} catch (error) {
let mensajeError;
switch (error.code) {
case 'ER_DUP_ENTRY':
mensajeError = 'Ya existe un producto con esos datos únicos';
break;
case 'ER_NO_REFERENCED_ROW_2':
mensajeError = 'La categoría especificada no existe';
break;
case 'ER_BAD_NULL_ERROR':
mensajeError = 'Faltan campos obligatorios';
break;
case 'ER_DATA_TOO_LONG':
mensajeError = 'Algún campo excede la longitud máxima permitida';
break;
default:
mensajeError = `Error de base de datos: ${error.message}`;
}
return {
exito: false,
error: error.code,
mensaje: mensajeError
};
} finally {
await connection.end();
}
}
// Uso con manejo completo de errores
const producto = {
nombre: 'Monitor 4K',
precio: 399.99,
stock: 10,
categoria_id: 1
};
insertarConManejoErrores(producto)
.then(resultado => {
if (resultado.exito) {
console.log('✓', resultado.mensaje, '- ID:', resultado.id);
} else {
console.error('✗', resultado.mensaje);
}
});
Las sentencias INSERT con mysql2 proporcionan todas las herramientas necesarias para agregar datos de manera segura y eficiente a bases de datos MySQL desde Node.js. La combinación de consultas parametrizadas, validación de datos y manejo apropiado de errores garantiza la integridad y confiabilidad de las operaciones de inserción.
Otras lecciones de Node
Accede a todas las lecciones de Node y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Instalación De Node.js
Introducción Y Entorno
Fundamentos Del Entorno Node.js
Introducción Y Entorno
Estructura De Proyecto Y Package.json
Introducción Y Entorno
Introducción A Node
Introducción Y Entorno
Gestor De Versiones Nvm
Introducción Y Entorno
Repl De Nodejs
Introducción Y Entorno
Ejercicios de programación de Node
Evalúa tus conocimientos de esta lección Módulo mysql2 con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.