Aprende SQL

MySQL con Node.js: instalación y uso paso a paso

SQLite es genial para empezar, pero muchas aplicaciones web necesitan un motor cliente-servidor: una base de datos que corre como un proceso aparte, acepta conexiones por red y da servicio a muchos usuarios a la vez. En esta lección instalamos MySQL y lo conectamos con Node.js paso a paso.

🟢 Tu app Node.js
🐬 Servidor MySQL :3306
🗄️ tienda tablas
Tu código nunca toca los archivos: habla por red con un servidor que gestiona los datos.

A diferencia de SQLite, aquí tu programa no abre un archivo: se conecta a un servidor MySQL (por defecto en el puerto 3306), le manda las consultas y recibe los resultados.

Paso 1 · Instalar MySQL

Tienes varias formas de tener un MySQL funcionando. Elige la que más te convenga.

Opción A · Docker (la más rápida y recomendada)

Si tienes Docker, no necesitas instalar MySQL en tu sistema: lo levantas en un contenedor aislado. Es la forma más limpia y funciona igual en macOS, Linux y Windows.

docker run --name mysql-curso \
  -e MYSQL_ROOT_PASSWORD=supersecreto \
  -e MYSQL_DATABASE=tienda \
  -p 3306:3306 \
  -d mysql:8

Esto arranca MySQL 8, crea una base de datos llamada tienda y la expone en localhost:3306. Para pararlo o volver a arrancarlo:

docker stop mysql-curso
docker start mysql-curso

Opción B · Instalación nativa

Si prefieres instalarlo directamente en tu máquina:

# macOS (con Homebrew)
brew install mysql
brew services start mysql

# Ubuntu / Debian
sudo apt update && sudo apt install mysql-server
sudo systemctl start mysql

En Windows, descarga el MySQL Installer desde la web oficial y sigue el asistente. Tras instalar, es muy recomendable ejecutar mysql_secure_installation para poner una contraseña de root y endurecer la configuración.

Paso 2 · Crear la base de datos

Conéctate al servidor con el cliente de línea de comandos (si usaste Docker con MYSQL_DATABASE=tienda, este paso ya está hecho):

mysql -u root -p

Y crea la base de datos y, opcionalmente, un usuario específico para tu aplicación (mejor que usar root):

CREATE DATABASE IF NOT EXISTS tienda;

CREATE USER 'app'@'localhost' IDENTIFIED BY 'clave_de_app';
GRANT ALL PRIVILEGES ON tienda.* TO 'app'@'localhost';
FLUSH PRIVILEGES;

Paso 3 · Preparar el proyecto de Node

Crea una carpeta, inicia el proyecto e instala el driver oficial recomendado: mysql2. Es el paquete estándar para hablar con MySQL desde Node, con soporte de promesas y sentencias preparadas.

mkdir tienda-api && cd tienda-api
npm init -y
npm install mysql2

A diferencia de node:sqlite, MySQL necesita una dependencia externa (mysql2), porque Node no trae un cliente de MySQL incorporado. Seguiremos usando TypeScript ejecutable directamente con node app.ts en Node 24.

Paso 4 · Conectar desde Node.js

Usamos siempre la variante con promesas: mysql2/promise. Así podemos usar async/await, como en cualquier otra operación asíncrona de Node.

import mysql from "mysql2/promise"

const connection = await mysql.createConnection({
  host: "localhost",
  port: 3306,
  user: "app",
  password: "clave_de_app",
  database: "tienda",
})

console.log("✅ Conectado a MySQL")

Nunca escribas las credenciales en el código. En un proyecto real van en variables de entorno (process.env.DB_PASSWORD), no escritas a mano como en estos ejemplos didácticos.

Paso 5 · Crear una tabla

Con la conexión abierta, ejecutamos SQL con connection.execute(). Fíjate en la sintaxis de MySQL que ya viste en el curso: AUTO_INCREMENT, VARCHAR(n) y DECIMAL.

await connection.execute(`
  CREATE TABLE IF NOT EXISTS products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    stock INT NOT NULL DEFAULT 0
  )
`)

Paso 6 · Insertar datos (de forma segura)

Como en SQLite, usamos marcadores ? para pasar los valores. mysql2 los envía por separado y evita la inyección SQL. execute() devuelve un array; el primer elemento trae información del resultado.

const [result] = await connection.execute(
  "INSERT INTO products (name, price, stock) VALUES (?, ?, ?)",
  ["Teclado mecánico", 79.99, 25]
)

console.log(result.insertId)     // id autogenerado
console.log(result.affectedRows) // 1

Cuidado con la inyección SQL. Aquí es donde más importa: MySQL suele estar detrás de una aplicación web con datos que llegan de usuarios reales. Jamás construyas la consulta pegando esos datos con + o template strings (`... ${dato} ...`); un atacante podría colar ' OR '1'='1 y saltarse tus filtros. Pasa siempre los valores como parámetros con ?, tal y como hacemos aquí. Lo vimos en detalle en la lección de SQLite en Node.js.

Paso 7 · Consultar datos

Para un SELECT, el primer elemento del array son directamente las filas, como objetos de JavaScript:

const [rows] = await connection.execute(
  "SELECT id, name, price FROM products WHERE price < ? ORDER BY price",
  [100]
)

console.log(rows)
// [ { id: 1, name: 'Teclado mecánico', price: '79.99' } ]

En TypeScript conviene tipar las filas. mysql2 expone el tipo RowDataPacket, que puedes extender con tu propia forma:

import mysql from "mysql2/promise"
import type { RowDataPacket } from "mysql2"

interface Product extends RowDataPacket {
  id: number
  name: string
  price: string
  stock: number
}

const [products] = await connection.execute<Product[]>(
  "SELECT * FROM products"
)

for (const product of products) {
  console.log(`${product.name}: ${product.price} €`)
}

Paso 8 · Usar un pool de conexiones

Abrir una conexión por cada consulta es lento. En una aplicación real se usa un pool: un conjunto de conexiones reutilizables que mysql2 gestiona por ti. Es lo que querrás en cualquier servidor web.

import mysql from "mysql2/promise"

const pool = mysql.createPool({
  host: "localhost",
  user: "app",
  password: "clave_de_app",
  database: "tienda",
  waitForConnections: true,
  connectionLimit: 10, // hasta 10 conexiones simultáneas
})

// Usas el pool igual que una conexión, sin abrir ni cerrar nada
const [rows] = await pool.query("SELECT * FROM products")

El pool coge una conexión libre, ejecuta tu consulta y la devuelve al conjunto automáticamente.

execute vs query

mysql2 te ofrece dos métodos y conviene saber cuándo usar cada uno:

MétodoQué haceCuándo usarlo
execute()Usa sentencias preparadas. Manda SQL y valores por separado.Siempre que pases datos con ?. Es la opción segura por defecto.
query()Envía el SQL tal cual, sin preparar.Consultas sin parámetros o SQL dinámico controlado por ti.

En la práctica: usa execute() con ? para todo lo que involucre datos del usuario.

Una aplicación completa

Todo junto en un archivo tienda.ts, listo para node tienda.ts:

import mysql from "mysql2/promise"

const pool = mysql.createPool({
  host: "localhost",
  user: "app",
  password: "clave_de_app",
  database: "tienda",
  connectionLimit: 10,
})

// Crear la tabla si no existe
await pool.execute(`
  CREATE TABLE IF NOT EXISTS products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    stock INT NOT NULL DEFAULT 0
  )
`)

// Insertar un producto
await pool.execute(
  "INSERT INTO products (name, price, stock) VALUES (?, ?, ?)",
  ["Ratón inalámbrico", 29.99, 40]
)

// Consultar los productos más baratos
const [rows] = await pool.execute(
  "SELECT name, price FROM products WHERE price < ? ORDER BY price",
  [50]
)
console.table(rows)

// Cerramos el pool para que el programa termine
await pool.end()

Fíjate en lo importante: el SQL es lo mismo que aprendiste en el curso. Cambia el motor y el driver, pero SELECT, WHERE, ORDER BY o JOIN son idénticos. Lo que has aprendido se transfiere directamente.

Practica el SQL de la tienda

Practica sobre una versión de la tabla products (adaptada al playground, que usa SQLite):

Ejercicios

En resumen

  • MySQL es un motor cliente-servidor: tu app se conecta por red a un servidor en el puerto 3306.
  • La forma más cómoda de tener MySQL es con Docker; también puedes instalarlo de forma nativa.
  • En Node se usa el driver mysql2 (npm install mysql2), siempre en su versión con promesas: mysql2/promise.
  • Usa execute() con marcadores ? para consultas con datos, y un pool de conexiones en aplicaciones reales.
  • El SQL que has aprendido en el curso funciona igual: solo cambian el motor y el driver.