cd ~/blog

~/blog/programming/conectar-mariadb-mysql-express-node.md

Conecta tu proyecto a MariaDB/MySQL con Express y Node.js

programación 29-03-2025
tutorialnodeexpressmariadbmysqlbackend

¡Bienvenid@s a una nueva entrega sobre programación! En esta ocasión, he preparado un tutorial paso a paso en el que aprenderás a conectar un proyecto a MariaDB/MySQL, utilizando Express y Node.js. Si estás buscando una guía práctica para crear un CRUD funcional desde cero, estás en el lugar correcto. ¡Manos a la obra!

Conecta tu proyecto a MariaDB/MySQL con Express y Node.js

~9 min de lectura


Comenzaremos instalando el servidor de base de datos. Usaremos MariaDB, una alternativa mantenida por la comunidad que ofrece mejoras de rendimiento en ciertos casos, mayor transparencia y motores de almacenamiento adicionales. La instalación es similar a la de MySQL, aunque con algunas diferencias que tendrás en cuenta si prefieres este último.

A lo largo del tutorial, te mostraremos cómo trabajar tanto con MariaDB como con MySQL. Aunque el código para MySQL es completamente compatible con MariaDB, destacaremos las sutiles diferencias que pueden influir en la elección entre uno u otro.

Para comenzar, descargamos el servidor de MariaDB desde el siguiente enlace:

https://mariadb.com/downloads/community/

Elegimos nuestro sistema operativo y hacemos click en descargar:

Figura 01

Figura 01

No nos complicaremos con la instalación, ya que no es el fin de este tutorial, y daremos siguiente hasta llegar a esta pantalla:

Figura 02

Figura 02

En esta pantalla, estableceremos la contraseña para el usuario root, que actúa como administrador principal de la base de datos. En este tutorial, utilizaremos este usuario para ejecutar consultas y realizar cambios en la base de datos. Sin embargo, en un entorno de producción, es recomendable crear usuarios específicos con permisos limitados según su rol, con el objetivo de proteger la integridad y seguridad de los datos.

Continuamos con la instalación, haciendo click en “Next”, “Next”, “Install” y listo.

Ahora, crearemos la base de datos donde trabajaremos:

CREATE DATABASE ejemplo_db;
USE ejemplo_db;
CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100),
  email VARCHAR(100)
);

Con el comando CREATE DATABASE ejemplo_db, creamos la base de datos en el servidor. Luego, usamos el comando USE ejemplo_db para seleccionar la base de datos que acabamos de crear.

Una vez seleccionada la base de datos, creamos una tabla para guardar datos llamada “users”, el cual contendrá 3 campos:

  • id de tipo INT (numero entero), el cual se autoincrementará de manera automática con el comando AUTO_INCREMENT, al ingresar datos a la tabla, y este funcionará como PRIMARY KEY, que es un identificador único para cada fila en una tabla.

  • name y email, ambos de tipo VARCHAR (cadenas de texto variable), con un largo máximo de 100 caracteres cada uno.

Podemos ejecutar este código utilizando el administrador con GUI que incluye MariaDB, llamado HeidiSQL, o a través de líneas de comandos. Nosotros utilizaremos la opción por línea de comandos.

Yo utilizo Windows 10 pro y powershell, por lo que, si utilizas estos recursos, es muy probable que obtengas el siguiente error al intentar iniciar el prompt de MariaDB (si usas MacOS o GNU/Linux, puedes saltar esta sección):

Figura 03

Figura 03

Esto se debe a que no se encuentra agregada de manera automática la variable de entorno.

Para solucionarlo, podemos dirigirnos al path de la aplicación y ejecutar desde allí los comandos (C:\Program Files\MariaDB 11.*\bin), o agregar la variable de entorno de la siguiente manera:

  • Abrimos la configuración avanzada del sistema:

Figura 04

Figura 04

  • Al hacer click en variables de entorno, aparecerán dos secciones, una que muestra las variables de entorno del usuario, y otra que muestra las variables de entorno para todo el sistema (disponibles para todos los usuarios), nosotros la agregaremos al path de las variables del usuario, por lo que buscamos la variable “Path” y hacemos doble click:

Figura 05

Figura 05

Hacemos click en “Nuevo” y agregamos el path de mariadb, que en mi caso es C:\Program Files\MariaDB 11.7\bin:

Figura 06

Figura 06

Hacemos click en aceptar, y ya tenemos acceso a la prompt de MariaDB en nuestro powershell.

Ahora iniciamos la conexión y cargamos el script que creamos previamente:

 mariadb -u root -p

Les solicitará la contraseña que establecimos al instalar el servidor de la base de datos.

Una vez dentro del cliente de MariaDB, cargamos nuestro script con el siguiente comando:

MariaDB [(none)]> source \ruta\de\su\archivo\ejemplo_db.sql

Y para corroborar que se ha modificado la base de datos, ejecutamos el comando show tables:

MariaDB [ejemplo_db]> show tables;

Figura 07

Figura 07

Ya tenemos nuestra base de datos creada, por lo que ahora crearemos el backend que interactuara con ella.

Creamos un directorio llamado mariadb-connection, ingresamos al directorio e inicializamos el proyecto con pnpm (npm, yarn o el gestor que utilicen):

 mkdir mariadb-connection
 cd .\mariadb-connection\
pnpm init

Figura 08

Figura 08

Ahora instalaremos las dependencias que utilizaremos durante el desarrollo de nuestra app:

  • dotenv: para tener acceso a las variables de entorno definidas en el archivo .env.

  • express: para el manejo de rutas y la creación de nuestro servidor.

  • nodemon: para no tener que reiniciar el servidor cada vez que hagamos un cambio, ya que monitorea y reinicia de manera automática nuestro servidor.

  • mysql2 o mariadb: de acuerdo a que servidor hayan instalado, deberán instalar uno u otro, en mi caso instalaré mariadb.

 pnpm add dotenv express mariadb nodemon

Una vez instaladas las dependencias, abrimos nuestro proyecto con nuestro editor de preferencia.

Comenzaremos definiendo las variables de entorno de nuestro proyecto en el archivo .env, las cuales son las siguientes:

MARIADB_HOST = 'localhost'
MARIADB_USER = 'root'
MARIADB_PASSWORD = '<su_password>'
MARIADB_DATABASE = 'ejemplo_db'
PORT = 3001

Recuerden que nunca deben ingresar sus credenciales directamente en su código por motivos de seguridad.

Luego creamos un directorio llamado “config”, el cual contendrá la configuración para la conexión de nuestra base de datos, y dentro crearemos un archivo llamado db.js, el cual contendrá lo siguiente:

  • MariaDB
const mariadb = require('mariadb')
const pool = mariadb.createPool({
  host: process.env.MARIADB_HOST,
  user: process.env.MARIADB_USER,
  password: process.env.MARIADB_PASSWORD,
  database: process.env.MARIADB_DATABASE,
  connectionLimit: 5, // Límite de conexiones simultáneas
})
async function connect() {
  try {
    const conn = await pool.getConnection()
    console.log('Connected to MariaDB')
    conn.release() // Liberar la conexión
  } catch (error) {
    console.error('Error connecting to MariaDB:', error)
  }
}
connect()
module.exports = pool

Primero importamos el paquete mariadb, luego creamos un pool con createPool(), el cual mantiene abierta una cierta cantidad de conexiones para ser reutilizadas, y de esta manera mejorar el rendimiento, con los datos del archivo .env.

A continuación, creamos una función asíncrona que llamamos connect() (pueden nombrarla de otra manera), para establecer la conexión y mostrar un mensaje de éxito en caso de que se establezca la conexión, o un mensaje de error en caso de que algo haya salido mal.

Finalmente, exportamos el pool.

  • MySQL
const mysql = require('mysql2')
const connection = mysql.createConnection({
  host: process.env.MARIADB_HOST,
  user: process.env.MARIADB_USER,
  password: process.env.MARIADB_PASSWORD,
  database: process.env.MARIADB_DATABASE,
})
connection.connect((err) => {
  if (err) {
    console.error('Error connecting to MySQL:', err)
    return
  }
  console.log('Connected to MySQL')
})
module.exports = connection

La conexión con MySQL es muy similar a MariaDB, pero esta vez no creamos un pool de conexiones, sino que creamos solo una que se utilizará para crear consultas a la base de datos.

Luego utilizamos el método connect() para ejecutar la conexión y mostrar los mensajes de error o éxito al usuario a través de la consola.

Finalmente exportamos la conexión.

Ahora crearemos otro directorio, el cual contendrá las rutas para interactuar con la base de datos, llamado routes, el cual contendrá un archivo llamado users.js.

Hasta el momento, nuestro proyecto debería verse así:

Figura 09

Figura 09

Dentro de users.js crearemos las rutas para interactuar con la base de datos (estas son compatibles con MySQL y MariaDB), y comenzaremos con la ruta para agregar un usuario a la base de datos:

const express = require('express')
const router = express.Router()
const db = require('../config/db')

Importamos express, router para el manejo de rutas y db que contiene la conexión que realizamos anteriormente, ahora creamos el método:

router.post('/api/users', async (request, response) => {
  const { name, email } = request.body
  if (!name || !email) {
    return response.status(400).json({ message: 'Parameters are missing' })
  }
  try {
    const { insertId } = await db.execute('INSERT INTO users (name, email) VALUES (?, ?)', [
      name,
      email,
    ])
    response.status(201).json({
      message: 'User created',
      id: Number(insertId),
    })
  } catch (error) {
    response.status(500).json({
      message: 'Error creating user',
      error: error.message,
    })
  }
})
module.exports = router

Con router.post(), configuramos el servidor para escuchar las peticiones HTTP POST que se envíen al endpoint especificado, luego extraemos las variables name y email del cuerpo de la request con request.body.

Para ejecutar la consulta SQL que inserta estos datos en la base de datos, utilizamos el método db.execute(). Este método devuelve un objeto que contiene el resultado de la operación. Usamos la desestructuración con const {insertId} para extraer y almacenar la ID del valor insertado, en la variable insertId (con el cliente mysql2 funciona un poco diferente, ya que no devuelve un objeto sino un arreglo, pero la lógica es muy similar).

A continuación, enviamos una respuesta con un código HTTP 201, que indica que el recurso se creó con éxito. En esta respuesta, incluimos un mensaje de éxito y la ID del registro insertado. Si ocurre algún error durante la operación, enviamos un código HTTP 500 acompañado de un mensaje que detalla el problema, y finalmente exportamos el router para ser utilizado por nuestra app:

Figura 10

Figura 10

Para probar nuestro método, crearemos un fichero en la raíz de nuestro proyecto, llamado app.js, el cual contendrá la configuración de nuestro servidor.

Primero importamos lo necesario para ejecutar nuestro servidor, lo cual es dotenv para tener acceso a .env, express y las rutas que definimos anteriormente:

require('dotenv').config()
const express = require('express')
const userRoutes = require('./routes/users')
const app = express()

Para que express pueda manejar datos json en el cuerpo de las requests, debemos incorporar un middleware, los cuales son funciones que se ejecutan entre la recepción de una solicitud HTTP y la respuesta final que se envía al cliente:

app.use(express.json())

Luego incorporamos las rutas a nuestra app:

app.use('/', userRoutes)

Y finalmente, obtenemos el PORT del archivo .env y ponemos nuestra app en escucha en ese puerto:

const PORT = process.env.PORT
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})

Para que nuestro servidor detecte los cambios, utilizaremos nodemon y crearemos un script personalizado en el package.json:

{

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "nodemon app.js"
  },

}

El script "dev": "nodemon app.js", nos permite ejecutar nuestra app con el comando pnpm dev y que se reinicie de manera automática si detecta cambios en el:

Figura 11

Figura 11

¡Ya tenemos nuestra app corriendo y conectada al servidor de MariaDB!

Para probar nuestra app podemos utilizar aplicaciones como postman, insomnia, curl, etc. Nosotros utilizaremos una extensión de Visutal Studio Code, llamada REST Client.

REST Client es una extensión de Visual Studio Code que permite enviar y probar solicitudes HTTP directamente desde el editor, lo que resulta muy útil para verificar nuestras rutas y APIs.

Creamos un nuevo directorio en nuestra app, llamado requests, donde guardaremos las peticiones que realizaremos.

Creamos el archivo para probar nuestro método:

POST http://localhost:3001/api/users
Content-Type: application/json

{
    "name": "testUser",
    "email": "test@test.com"
}

Primero indicamos que haremos una solicitud POST e indicamos la ruta que definimos previamente, luego especificamos que el contenido que enviaremos es de tipo json, y finalmente el cuerpo de nuestra solicitud, la cual contiene el campo y la información que enviaremos al servidor.

Con la extensión REST Client, aparecerá un botón con el mensaje “Send Request” y nos mostrará el resultado de nuestra request:

HTTP/1.1 201 Created
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 33
ETag: W/"21-6wuUOqTuv5WcdCjnfU0EIzuq0aY"
Date: Fri, 28 Mar 2025 22:42:25 GMT
Connection: close

{
  "message": "User created",
  "id": 1
}

Ahora crearemos la ruta para obtener todos los datos de la tabla users:

router.get('/api/users', async (request, response) => {
  try {
    const rows = await db.execute('SELECT * FROM users')
    response.json(rows)
  } catch (error) {
    response.status(500).json({
      message: 'Error getting users',
      error: error.message,
    })
  }
})

Esta vez, indicamos que ejecutaremos una solicitud HTTP GET, almacenamos el resultado de la consulta en const rows y las enviamos al cliente como json. En caso de algún error, respondemos con un código HTTP 500 y el detalle del error.

A continuación, crearemos el método para obtener solo 1 usuario según su ID:

router.get('/api/users/:id', async (request, response) => {
  try {
    const id = request.params.id
    const row = await db.execute('SELECT * FROM users WHERE id=?', [id])
    response.json(row)
  } catch (error) {
    response.status(500).json({
      message: 'Error getting user',
      error: error.message,
    })
  }
})

Este método es muy similar al anterior, pero esta vez, obtenemos la ID desde los parámetros de la request con request.params.id, y la agregamos a la consulta. Guardamos el resultado en const row, y la enviamos al cliente. En caso de que ocurra algún error, le enviamos el detalle al cliente.

Continuamos con el método para actualizar un valor:

router.put('/api/users/:id', async (request, response) => {
  try {
    const id = request.params.id
    const { name, email } = request.body
    const { affectedRows } = await db.execute('UPDATE users SET name = ?, email = ? WHERE id = ?', [
      name,
      email,
      id,
    ])
    if (affectedRows > 0) {
      response.json({ message: 'User updated successfully' })
    } else {
      response.status(404).json({ message: 'User not found' })
    }
  } catch (error) {
    response.status(500).json({
      message: 'Error updating user',
      error: error.message,
    })
  }
})

En este método, obtenemos la ID desde los parámetros de la request con request.params.id y los campos a actualizar con const { name, email } = request.body, en este caso de uso actualizaremos ambos datos. Luego almacenamos las filas afectadas en const { affectedRows }, para mostrar un mensaje de error si no se afectaron filas, ya que este es un indicio de que el usuario no existe, y en caso de error enviamos el mensaje correspondiente.

Finalmente creamos el método para eliminar usuarios:

router.delete('/api/users/:id', async (request, response) => {
  try {
    const id = request.params.id
    const { affectedRows } = await db.execute('DELETE FROM users WHERE id = ?', [id])
    if (affectedRows > 0) {
      response.json({ message: 'User deleted successfully' })
    } else {
      response.status(404).json({ message: 'User not found' })
    }
  } catch (error) {
    response.status(500).json({
      message: 'Error deleting user',
      error: error.message,
    })
  }
})

Obtenemos el ID de los parámetros de la request con request.params.id, almacenamos la cantidad de filas afectadas, ya que al igual que en el caso anterior, esto nos indica se existe el usuario o no, y en cada caso enviar el mensaje correspondiente. En caso de errores, mostramos el mensaje correspondiente.

El código completo:

Figura 12

Figura 12

De todas maneras, el código esta disponible en este repositorio, donde encontraran los archivos .rest para probar su aplicación y el script para crear la base de datos:

https://github.com/wh01s17/mariadb-connection

¡Y eso es todo por esta vez! En este tutorial, creamos un CRUD simple utilizando Node.js, Express y MariaDB. Este proyecto básico es un excelente punto de partida que puedes personalizar y expandir según las necesidades de tu aplicación. Ya sea añadiendo más funcionalidades, mejorando las validaciones o integrando nuevas herramientas, tienes una base sólida para seguir construyendo.

Gracias por leer, ¡nos vemos en la próxima entrega!

// relacionados