Conectar la base de datos al servidor Express
Hasta ahora hemos aprendido dos cosas por separado: cómo crear un servidor API con Express y cómo gestionar una base de datos con SQL. Ahora vamos a integrar ambos.
En el ejemplo anterior de Express, los datos de muestra estaban en un array de JavaScript:
const samples = [
{ id: "S001", name: "Blood Sample A", od: 1.85, status: "pass" },
// ...
];Al reiniciar el servidor, esta matriz vuelve a su estado inicial. Si se registra una nueva muestra mediante POST, esta se perderá al reiniciar el servidor. Es como escribir en un cuaderno de laboratorio con lápiz y borrarlo cada vez.
Conectar una base de datos soluciona este problema. Si se configura Express para almacenar y recuperar los datos en la base de datos en lugar de en una matriz, los datos permanecerán seguros incluso si el servidor se apaga o se reinicia la computadora.
Preparación de la conexión: instalación del paquete mysql2
Para conectarse a MySQL desde Node.js, se requiere el paquete mysql2:
npm install mysql2Y luego, configura la conexión a la base de datos en el código:
const mysql = require("mysql2");
const db = mysql.createConnection({
host: "localhost",
user: "root",
password: "contraseña",
database: "lab_db"
});
db.connect(function(err) {
if (err) {
console.log("Error de conexión a la BD:", err.message);
return;
}
console.log("Conexión a la BD correcta");
});Es como iniciar sesión en un equipo de laboratorio: proporciona la dirección (host), las credenciales de la cuenta (usuario/contraseña) y la base de datos que se utilizará.
Reemplazar el arreglo con una base de datos: SELECT
En el código existente de Express, se obtienen los datos mediante una consulta SQL en lugar de un arreglo.
Antes (arreglo):
app.get("/samples", function(req, res) {
res.json(samples);
});After (DB):
app.get("/samples", function(req, res) {
db.query("SELECT * FROM samples", function(err, rows) {
if (err) {
res.status(500).json({ error: "Error al consultar la BD" });
return;
}
res.json(rows);
});
});db.query() envía consultas SQL a la base de datos y recibe los resultados a través de una función de devolución de llamada. rows devuelve los datos en forma de matriz, con la misma estructura que las matrices que se creaban manualmente. No es necesario modificar el código del lado del cliente.
Consulta de muestras específicas: cláusula WHERE y marcadores de posición
Al consultar una muestra específica mediante parámetros de URL, incluir directamente la entrada del usuario en la consulta SQL puede generar una vulnerabilidad de inyección SQL. Utilice marcadores de posición (?):
app.get("/sample/:id", function(req, res) {
db.query(
"SELECT * FROM samples WHERE id = ?",
[req.params.id],
function(err, rows) {
if (err) {
res.status(500).json({ error: "Error en la consulta" });
return;
}
if (rows.length === 0) {
res.status(404).json({ error: "No se encontró la muestra" });
return;
}
res.json(rows[0]);
}
);
});En el lugar de ?, se inserta de forma segura el valor de [req.params.id]. De este modo, incluso si un usuario malintencionado intenta insertar código SQL en la URL, la base de datos lo tratará simplemente como datos.
Es como tener un campo en el protocolo donde solo se puede modificar el número de muestra: cualquier cosa que se introduzca en ese campo se interpretará únicamente como el número de muestra, sin que se pueda modificar el protocolo en sí.
Registro de la muestra: INSERT
Realiza una solicitud POST para registrar una nueva muestra y guardarla de forma permanente en la base de datos:
app.use(express.json());
app.post("/samples", function(req, res) {
const { name, od, status } = req.body;
db.query(
"INSERT INTO samples (name, od, status, created_at) VALUES (?, ?, ?, NOW())",
[name, od, status],
function(err, result) {
if (err) {
res.status(500).json({ error: "Error en el registro" });
return;
}
res.json({
message: "Muestra registrada correctamente",
id: result.insertId
});
}
);
});result.insertId es el ID que se genera automáticamente para la fila que se acaba de añadir. NOW() es una función de MySQL que inserta automáticamente la hora actual.
Ejemplo completo: API CRUD para la gestión de muestras
Aquí tienes el código completo del servidor que integra todo lo aprendido hasta ahora:
const express = require("express");
const mysql = require("mysql2");
const app = express();
app.use(express.json());
const db = mysql.createConnection({
host: "localhost",
user: "root",
password: "contraseña",
database: "lab_db"
});
// Lista de todas las muestras
app.get("/samples", function(req, res) {
db.query("SELECT * FROM samples ORDER BY created_at DESC", function(err, rows) {
if (err) return res.status(500).json({ error: err.message });
res.json(rows);
});
});
// Consultar una muestra concreta
app.get("/sample/:id", function(req, res) {
db.query("SELECT * FROM samples WHERE id = ?", [req.params.id], function(err, rows) {
if (err) return res.status(500).json({ error: err.message });
if (rows.length === 0) return res.status(404).json({ error: "Muestra no encontrada" });
res.json(rows[0]);
});
});
// Registrar una muestra
app.post("/samples", function(req, res) {
const { name, od, status } = req.body;
db.query(
"INSERT INTO samples (name, od, status, created_at) VALUES (?, ?, ?, NOW())",
[name, od, status],
function(err, result) {
if (err) return res.status(500).json({ error: err.message });
res.json({ message: "Registro completado", id: result.insertId });
}
);
});
// Filtrar solo las muestras que superaron el QC
app.get("/samples/passed", function(req, res) {
db.query("SELECT * FROM samples WHERE status = 'pass'", function(err, rows) {
if (err) return res.status(500).json({ error: err.message });
res.json({ count: rows.length, samples: rows });
});
});
app.listen(3000, function() {
console.log("Servidor de la API de gestión de muestras: http://localhost:3000");
});Lo especial de este servidor es que comparte la misma interfaz API que el servidor Express basado en arreglos. /samples, /sample/:id, /samples/passed; mismas direcciones, mismo formato de respuesta. Lo único que ha cambiado es el almacenamiento interno. El código del frontend no requiere ninguna modificación.
Esta es la mayor ventaja de separar el backend del frontend. Aunque cambies el repositorio de archivos a MySQL y luego a PostgreSQL, si la API se mantiene igual, el frontend no se verá afectado.
Prueba tú mismo (Ejemplo)
Completa el siguiente espacio en blanco para completar la ruta de Express que consulta las muestras de un investigador específico.
app.get("/researcher/:name/samples", function(req, res) {db.("SELECT * FROM samples WHERE researcher = ",[req..name],function(err, rows) {if (err) return res.status(500).json({ error: err.message });res.json(rows);});});
Errores comunes y soluciones
P: Ocurre el error ER_ACCESS_DENIED_ERROR: Access denied for user
Comprueba que el usuario, la contraseña y la base de datos de createConnection sean correctos. Debe existir una cuenta en MySQL y tener permisos de acceso a la base de datos correspondiente.
P: Ocurre el error ECONNREFUSED
El servidor de MySQL no está en ejecución. En macOS, inicia MySQL con brew services start mysql; en Linux, hazlo con sudo systemctl start mysql.
P: El resultado de la consulta devuelve un array vacío []
No hay datos en la tabla o no hay filas que cumplan con la condición de WHERE. Primero, ejecuta directamente SELECT * FROM samples; en el cliente de MySQL para verificar si existen datos.
P: Los datos en coreano se guardan de forma incorrecta
Comprueba que el conjunto de caracteres (charset) de la base de datos y las tablas sea utf8mb4. Puedes cambiarlo a ALTER DATABASE lab_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;.