libro / blog / nodejs-mysql-mongodb

// bases de datos

Conectar Node.js con MySQL y MongoDB: guía práctica

Gustavo Arias10 min de lectura

TL;DR Para MySQL usa mysql2 con un pool de conexiones y consultas con marcadores (?), nunca concatenando valores. Para MongoDB, el driver oficial con un solo MongoClient reutilizado en toda la aplicación. Elige MySQL cuando los datos son relacionales y la integridad es crítica; MongoDB cuando los documentos tienen estructura variable.

Una API sin base de datos olvida todo en cuanto se reinicia. Node.js se lleva bien tanto con bases de datos relacionales como con bases de documentos, y en muchos proyectos reales conviven las dos. En el libro Node.js desde cero trabajamos con MySQL y con MongoDB; acá tenés cómo conectar cada una de forma correcta y el mismo CRUD escrito para ambas.

MySQL o MongoDB: cuál elegir

MySQL (relacional)MongoDB (documentos)
EstructuraTablas con columnas fijasDocumentos JSON con forma flexible
RelacionesClaves foráneas y JOINDocumentos anidados o referencias
IntegridadFuerte: tipos, restricciones, transaccionesMás flexible; también admite transacciones
Ideal paraPedidos, facturación, usuarios y permisosCatálogos variables, contenido, logs, eventos

Una regla práctica: si los datos tienen relaciones claras y la consistencia es crítica (dinero, stock), empezá por una base relacional. Si cada registro puede tener campos distintos y se lee casi siempre entero, MongoDB encaja muy bien.

MySQL con mysql2

$ npm install mysql2
import mysql from 'mysql2/promise';

// Un pool reutiliza conexiones: crealo UNA vez y compartilo en toda la app
export const pool = mysql.createPool({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  connectionLimit: 10,
});

export const postsMySQL = {
  async listar() {
    const [filas] = await pool.query('SELECT id, titulo, publicado FROM posts ORDER BY id DESC');
    return filas;
  },

  async buscarPorId(id) {
    // execute() usa sentencias preparadas: el valor viaja separado del SQL
    const [filas] = await pool.execute('SELECT * FROM posts WHERE id = ?', [id]);
    return filas[0] ?? null;
  },

  async crear({ titulo, contenido }) {
    const [res] = await pool.execute(
      'INSERT INTO posts (titulo, contenido) VALUES (?, ?)',
      [titulo, contenido],
    );
    return { id: res.insertId, titulo, contenido };
  },

  async borrar(id) {
    const [res] = await pool.execute('DELETE FROM posts WHERE id = ?', [id]);
    return res.affectedRows > 0;
  },
};
Nunca armes SQL concatenando datos del usuario (`... WHERE id = ${req.params.id}`): es la puerta de entrada de la inyección SQL. Los marcadores ? resuelven el problema.

Transacciones

Cuando varias operaciones tienen que ocurrir juntas o ninguna, usá una conexión del pool y una transacción:

import { pool } from './mysql.js';

export async function crearPedido(usuarioId, productoId, cantidad) {
  const conn = await pool.getConnection();
  try {
    await conn.beginTransaction();
    const [r] = await conn.execute(
      'UPDATE productos SET stock = stock - ? WHERE id = ? AND stock >= ?',
      [cantidad, productoId, cantidad],
    );
    if (r.affectedRows === 0) throw new Error('Stock insuficiente');
    await conn.execute(
      'INSERT INTO pedidos (usuario_id, producto_id, cantidad) VALUES (?, ?, ?)',
      [usuarioId, productoId, cantidad],
    );
    await conn.commit();
  } catch (err) {
    await conn.rollback();
    throw err;
  } finally {
    conn.release();   // devolver la conexión al pool, pase lo que pase
  }
}

MongoDB con el driver oficial

$ npm install mongodb
import { MongoClient, ObjectId } from 'mongodb';

// Un solo cliente para toda la aplicación: gestiona su propio pool interno
const client = new MongoClient(process.env.MONGODB_URI);
await client.connect();
const db = client.db('blog');
const coleccion = db.collection('posts');

// Índice para las búsquedas frecuentes (se crea una vez; si ya existe, no hace nada)
await coleccion.createIndex({ publicado: 1, creadoEn: -1 });

export const postsMongo = {
  listar() {
    return coleccion.find({ publicado: true }).sort({ creadoEn: -1 }).limit(20).toArray();
  },

  buscarPorId(id) {
    if (!ObjectId.isValid(id)) return null;
    return coleccion.findOne({ _id: new ObjectId(id) });
  },

  async crear({ titulo, contenido, etiquetas = [] }) {
    const doc = { titulo, contenido, etiquetas, publicado: false, creadoEn: new Date() };
    const { insertedId } = await coleccion.insertOne(doc);
    return { _id: insertedId, ...doc };
  },

  async borrar(id) {
    if (!ObjectId.isValid(id)) return false;
    const { deletedCount } = await coleccion.deleteOne({ _id: new ObjectId(id) });
    return deletedCount > 0;
  },
};

export { client };
MongoDB también tiene su versión de la inyección: si pasás req.body directamente como filtro, un atacante puede enviar { "password": { "$ne": null } }. Construí siempre los filtros con valores validados, nunca con objetos enteros que vengan del cliente.

Si preferís trabajar con modelos y esquemas, Mongoose añade validación y una capa de modelos encima del driver; para empezar, el driver oficial es más directo y te enseña cómo funciona MongoDB de verdad.

Configuración con variables de entorno

DB_HOST=localhost
DB_USER=blog_app
DB_PASSWORD=una-clave-larga-y-unica
DB_NAME=blog
MONGODB_URI=mongodb://localhost:27017

Desde Node.js 20.6 podés cargarlo sin dependencias: node --env-file=.env src/server.js. Añadí .env a .gitignore y documentá las variables en un .env.example sin valores reales.

Errores comunes

Para validar los datos antes de guardarlos, revisá validación y manejo de errores.

Datos relacionales y NoSQL en un mismo libro

El libro Node.js desde cero enseña a conectar aplicaciones a bases de datos relacionales y NoSQL, MySQL y MongoDB, y a usarlas en sus proyectos.

Comprar en Amazon