Hablar con la base de datos
Un servidor casi nunca guarda los datos en memoria: los persiste en una base de datos (PostgreSQL, MySQL, SQLite...). Para hablar con ella, Node usa un driver: una librería que abre conexiones y envía consultas SQL.
Pools de conexión
Abrir una conexión es caro. Por eso no se abre una por petición, sino que se mantiene un pool (un conjunto de conexiones reutilizables). Cada consulta toma prestada una conexión del pool y la devuelve al terminar:
import { Pool } from "pg";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
Consultas asíncronas
Hablar con la base de datos es una operación de E/S: tarda y no debe
bloquear el hilo. Por eso las consultas son asíncronas y se esperan con
async/await:
async function buscarUsuario(id) {
const { rows } = await pool.query(
"SELECT * FROM usuarios WHERE id = $1",
[id]
);
return rows[0];
}
Fíjate en
$1y el array[id]: son parámetros. Nunca concatenes valores en el SQL (riesgo de inyección SQL); pásalos como parámetros.
ORMs
Escribir SQL a mano es flexible pero verboso. Un ORM (Object-Relational Mapper) traduce entre tablas y objetos de tu lenguaje. Los más usados en Node son Prisma, Sequelize y TypeORM. Aportan tres ideas:
- Modelos: describes cada entidad una vez (campos y tipos) y el ORM genera el acceso. Con TypeScript, las consultas quedan tipadas.
- Migraciones: los cambios del esquema (crear/alterar tablas) se versionan como archivos reproducibles, en lugar de tocar la base a mano.
- Consultas tipadas: en vez de SQL en texto, llamas a métodos:
// Estilo Prisma (conceptual)
const usuario = await prisma.usuario.findUnique({ where: { id } });
const nuevo = await prisma.usuario.create({ data: { email } });
El ORM sigue siendo asíncrono por debajo: todo devuelve Promise. Un ORM
es comodidad y seguridad de tipos; para consultas muy específicas siempre puedes
bajar al SQL del driver.