Consultas SELECT en Node.js
Una vez establecida la conexión con MySQL, la operación más habitual es recuperar datos mediante consultas SELECT. El paquete mysql2 proporciona métodos optimizados para ejecutar consultas de forma segura utilizando sentencias preparadas, lo que protege tu aplicación contra inyecciones SQL y mejora el rendimiento cuando repites la misma consulta con distintos parámetros.
Requisitos previos
Necesitas tener el paquete mysql2 instalado y una conexión o pool configurado como se explicó en el artículo anterior. También necesitas una base de datos con datos de prueba. Utiliza el siguiente script SQL para crear la tabla y los datos que usaremos en los ejemplos:
CREATE DATABASE IF NOT EXISTS tienda;
USE tienda;
CREATE TABLE productos (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
categoria VARCHAR(50) NOT NULL,
precio DECIMAL(10, 2) NOT NULL,
stock INT DEFAULT 0,
activo TINYINT(1) DEFAULT 1,
fecha_creacion DATETIME DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO productos (nombre, categoria, precio, stock) VALUES
('Laptop HP Pavilion', 'Computadoras', 12999.99, 25),
('Mouse Logitech MX Master', 'Periféricos', 1599.00, 150),
('Teclado Mecánico Corsair K70', 'Periféricos', 2299.50, 80),
('Monitor Samsung 27"', 'Monitores', 6499.00, 40),
('Auriculares Sony WH-1000XM5', 'Audio', 5999.99, 60),
('Webcam Logitech C920', 'Periféricos', 1299.00, 200),
('SSD Samsung 1TB', 'Almacenamiento', 1899.00, 120),
('RAM Corsair 16GB DDR5', 'Componentes', 1499.00, 90);Código completo
Este ejemplo muestra cómo ejecutar una consulta SELECT parametrizada y procesar los resultados:
const mysql = require('mysql2/promise');
async function consultarProductos() {
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'tu_contraseña',
database: 'tienda'
});
// Consulta con parámetros usando sentencias preparadas
const categoria = 'Periféricos';
const [rows, fields] = await pool.execute(
'SELECT id, nombre, precio, stock FROM productos WHERE categoria = ? ORDER BY precio DESC',
[categoria]
);
console.log(`Productos en categoría "${categoria}":`);
console.log('Columnas:', fields.map(f => f.name));
rows.forEach(row => {
console.log(` [${row.id}] ${row.nombre} - $${row.precio} (${row.stock} en stock)`);
});
await pool.end();
}
consultarProductos().catch(console.error);Salida esperada:
Productos en categoría "Periféricos":
Columnas: [ 'id', 'nombre', 'precio', 'stock' ]
[3] Teclado Mecánico Corsair K70 - $2299.50 (80 en stock)
[2] Mouse Logitech MX Master - $1599.00 (150 en stock)
[6] Webcam Logitech C920 - $1299.00 (200 en stock)
Explicación paso a paso
El método execute() es la forma recomendada para ejecutar consultas en mysql2 porque utiliza sentencias preparadas del lado del servidor. Esto significa que MySQL compila la consulta una vez y luego la ejecuta con diferentes parámetros, lo que mejora tanto la seguridad como el rendimiento.
La llamada a execute() devuelve un array con dos elementos: el primero contiene las filas resultantes como un array de objetos JavaScript, y el segundo contiene los metadatos de las columnas. Cada fila es un objeto cuyas propiedades corresponden a los nombres de las columnas seleccionadas.
Los parámetros se pasan como un array en el segundo argumento, y cada signo ? en la consulta se reemplaza por el valor correspondiente en el array. Nunca concatenes valores directamente en la cadena SQL, ya que esto expone tu aplicación a inyecciones SQL.
Consulta sin parámetros
Cuando no necesitas parámetros, puedes usar query() o execute() sin el segundo argumento:
const [rows] = await pool.execute('SELECT * FROM productos WHERE activo = 1');
console.log('Total de productos activos:', rows.length);Múltiples parámetros
Puedes usar tantos placeholders como necesites. El orden de los valores en el array debe coincidir con el orden de los signos ? en la consulta:
const [rows] = await pool.execute(
'SELECT nombre, precio FROM productos WHERE precio BETWEEN ? AND ? AND categoria = ?',
[1000, 5000, 'Periféricos']
);
rows.forEach(row => {
console.log(`${row.nombre}: $${row.precio}`);
});Salida esperada:
Teclado Mecánico Corsair K70: $2299.50
Mouse Logitech MX Master: $1599.00
Webcam Logitech C920: $1299.00
Obtener una sola fila
Si sabes que tu consulta devolverá una sola fila, puedes acceder directamente al primer elemento del array:
const [rows] = await pool.execute(
'SELECT nombre, precio, stock FROM productos WHERE id = ?',
[1]
);
if (rows.length > 0) {
const producto = rows[0];
console.log(`Producto: ${producto.nombre}`);
console.log(`Precio: $${producto.precio}`);
console.log(`Stock: ${producto.stock} unidades`);
} else {
console.log('Producto no encontrado');
}Salida esperada:
Producto: Laptop HP Pavilion
Precio: $12999.99
Stock: 25 unidades
Metadatos de las columnas
El segundo elemento devuelto por execute() contiene información útil sobre las columnas del resultado:
const [rows, fields] = await pool.execute('SELECT id, nombre, precio FROM productos LIMIT 1');
fields.forEach(field => {
console.log(`Columna: ${field.name}, Tipo: ${field.columnType}, Longitud: ${field.columnLength}`);
});Caso práctico
En una aplicación real, las consultas suelen incluir paginación, búsqueda y filtros. Veamos cómo construir una función de búsqueda de productos completa:
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'tu_contraseña',
database: 'tienda'
});
async function buscarProductos({ busqueda, categoria, precioMin, precioMax, pagina = 1, porPagina = 10 }) {
let sql = 'SELECT id, nombre, categoria, precio, stock FROM productos WHERE activo = 1';
const params = [];
if (busqueda) {
sql += ' AND nombre LIKE ?';
params.push(`%${busqueda}%`);
}
if (categoria) {
sql += ' AND categoria = ?';
params.push(categoria);
}
if (precioMin !== undefined) {
sql += ' AND precio >= ?';
params.push(precioMin);
}
if (precioMax !== undefined) {
sql += ' AND precio <= ?';
params.push(precioMax);
}
// Contar total de resultados
const sqlCount = sql.replace(
'SELECT id, nombre, categoria, precio, stock',
'SELECT COUNT(*) AS total'
);
const [countResult] = await pool.execute(sqlCount, params);
const total = countResult[0].total;
// Agregar paginación
sql += ' ORDER BY nombre ASC LIMIT ? OFFSET ?';
params.push(porPagina, (pagina - 1) * porPagina);
const [rows] = await pool.execute(sql, params);
return {
datos: rows,
paginacion: {
pagina,
porPagina,
total,
totalPaginas: Math.ceil(total / porPagina)
}
};
}
// Uso de la función
async function main() {
const resultado = await buscarProductos({
categoria: 'Periféricos',
precioMin: 1000,
precioMax: 3000,
pagina: 1,
porPagina: 5
});
console.log(`Mostrando ${resultado.datos.length} de ${resultado.paginacion.total} resultados`);
console.log(`Página ${resultado.paginacion.pagina} de ${resultado.paginacion.totalPaginas}`);
console.log('---');
resultado.datos.forEach(p => {
console.log(`${p.nombre} (${p.categoria}) - $${p.precio}`);
});
await pool.end();
}
main().catch(console.error);Salida esperada:
Mostrando 3 de 3 resultados
Página 1 de 1
---
Mouse Logitech MX Master (Periféricos) - $1599.00
Teclado Mecánico Corsair K70 (Periféricos) - $2299.50
Webcam Logitech C920 (Periféricos) - $1299.00
Manejo de errores
Las consultas SELECT pueden fallar por errores de sintaxis SQL, tablas inexistentes o problemas de conexión. Es importante capturar estos errores y proporcionar mensajes útiles:
async function consultaSegura(sql, params = []) {
try {
const [rows] = await pool.execute(sql, params);
return rows;
} catch (error) {
if (error.code === 'ER_NO_SUCH_TABLE') {
console.error('La tabla especificada no existe');
} else if (error.code === 'ER_PARSE_ERROR') {
console.error('Error de sintaxis en la consulta SQL');
} else if (error.code === 'ER_BAD_FIELD_ERROR') {
console.error('Una de las columnas especificadas no existe');
} else {
console.error('Error en la consulta:', error.message);
}
return [];
}
}
// Manejo de resultados vacíos
const productos = await consultaSegura(
'SELECT * FROM productos WHERE categoria = ?',
['Categoría Inexistente']
);
if (productos.length === 0) {
console.log('No se encontraron productos con los criterios especificados');
}Ahora que dominas las consultas SELECT, en el siguiente artículo aprenderás a insertar nuevos registros en tus tablas MySQL desde Node.js.
Escrito por Eduardo Lázaro
