3.4 · SQL con Node : PostgreSQL, MySQL, Prisma

Nivel 3 · Express y bases de datos

3.4SQL con Node : PostgreSQL, MySQL, Prisma

Objetivo: saber persistir con una base relacional (PostgreSQL/MySQL) vía un ORM moderno (Prisma), escribiendo consultas seguras — completando vuestro dominio de ambas familias de bases.
Tiempo estimado: 11 min · lectura autónoma · Si solo tienes 5 min: lee «Lo esencial en 30s» + el esquema.

Lo esencial en 30 segundos

  • Lo relacional estructura : tablas, filas, claves foráneas (PostgreSQL/MySQL), esquema estricto y transacciones ACID para la integridad.
  • El SQL se escribe o se genera : SELECT/INSERT/UPDATE/DELETE + JOIN en bruto parametrizado, o métodos de ORM (Prisma/Sequelize) que generan un SQL seguro.
  • La seguridad no es negociable : jamás concatenación de datos de usuario (inyección SQL) — consultas parametrizadas u ORM + comprensión del SQL subyacente.

Mini-caso hilado — Salim factura en SQL sin fisuras

Salim, desarrollador junior en Argel, debe facturar : clientes, pedidos y líneas vinculadas, totales por garantizar. El NoSQL flexible ya no basta. Modela tres tablas unidas por claves foráneas (PostgreSQL), escribe sus primeras consultas SELECT/JOIN en parametrizado, y luego pasa a Prisma : esquema declarativo schema.prisma, migración que versiona, cliente tipado prisma.commande.create() y findMany(). Cuando un campo de búsqueda contiene una comilla tramposa, la parametrización la neutraliza : ninguna inyección.

Último episodio del hilo Salim — de Express a ambas familias de bases : la persistencia completa.

1. El modelo relacional y el SQL

La familia SQL / relacional (sobre todo PostgreSQL, potente y open source, y MySQL/MariaDB) organiza los datos en tablas (columnas tipadas), filas (registros) y relaciones formales por claves foráneas (un pedido referencia a un cliente). El lenguaje SQL interroga : SELECT (leer), INSERT (crear), UPDATE, DELETE, JOIN (combinar tablas vinculadas). Garantías fuertes : esquema estricto y transacciones ACID para la coherencia — ideales para datos estructurados y críticos.

Desde Node, dos niveles : el driver en bruto (p. ej. pg para PostgreSQL) donde se escribe el SQL en cadenas — con el imperativo consultas parametrizadas ($1, $2…) pues concatenar entradas de usuario abre la inyección SQL, fallo crítico. O el ORM que genera el SQL en vuestro lugar.

2. Prisma : esquema, migraciones, cliente tipado

Un ORM (Object-Relational Mapper) mapea tablas ↔ objetos JS : se manipula prisma.client.create({ data }) y findMany({ where }) en vez de cadenas SQL — más legible, más productivo, y protegido de inyecciones por parametrización automática. Los corrientes : Prisma (moderno, muy apreciado) y Sequelize (antiguo, probado).

Prisma se distingue por su esquema declarativo (archivo schema.prisma que describe modelos y relaciones), sus migraciones (versionar la evolución del esquema) y su cliente generado y tipado (autocompletado, seguridad de tipos — excelente con TypeScript, sección 5-1). Recomendado para la mayoría de apps ; guardar el SQL en bruto para las consultas muy precisas. Reflejo de Salim : comprender el SQL generado (cargar las relaciones con eficacia, evitar el N+1) en vez de tratar el ORM como caja negra.

Error frecuente de interpretación: « Con un ORM, inútil aprender SQL, y concatenar una entrada en la consulta saca del apuro ». No. Sin bases SQL (joins, carga de relaciones), el ORM produce consultas ineficaces (N+1) incomprendidas. Y concatenar un dato de usuario, aunque sea « solo para probar », abre una inyección SQL explotable : parametrización siempre, sin excepción.
SQL con Node : dos niveles Del SQL directo al cliente tipado RelacionalTablas, filas, claves foráneas+Driver en brutoSQL parametrizado, jamás concatenado+ORM PrismaEsquema, migraciones, cliente tipado El ORM protege de inyecciones ; el SQL queda por comprender.
Del SQL directo al cliente tipado
Versión en texto del esquema (SEO / lectores de pantalla / traducción): 1) Relacional : Salim vincula clientes, pedidos y líneas por claves foráneas. 2) Driver en bruto : sus SELECT/JOIN pasan en consultas parametrizadas. 3) ORM : Prisma genera un cliente tipado desde schema.prisma con migraciones. Lección : productividad del ORM sin abandonar el SQL.

🔎 Ejemplo concreto — Prisma : un CRUD tipado y seguro

Salim crea y lista clientes sin escribir SQL.

// schema.prisma declara Client y Commande vinculados (1-N)
const nouveau = await prisma.client.create({
  data: { nom: 'Salim', email: 'salim@exemple-fictif.test' }
});
const actifs = await prisma.client.findMany({
  where: { actif: true }
});
// Driver bruto equivalente SEGURO (parametrizado, pg) :
// await pool.query('SELECT * FROM clients WHERE email = $1', [emailSaisi]);

Lectura : objetos y métodos lado Prisma, $1 parametrizado lado bruto.

Lección : ambas vías neutralizan la inyección ; la concatenación, jamás.

Vocabulario de la sección

SQL / relacional
Bases en tablas, filas y claves foráneas (PostgreSQL, MySQL) ; esquema estricto y transacciones ACID.

Ejemplo sencillo: Clientes, pedidos y líneas vinculados en casa de Salim.

Lo que no es: documentos flexibles sin esquema.

Reflejo: elegirlo para datos vinculados y críticos.

SQL (lenguaje)
SELECT (leer), INSERT (crear), UPDATE, DELETE, JOIN (combinar tablas vinculadas).

Ejemplo sencillo: SELECT con JOIN pedidos-clientes de Salim.

Lo que no es: llamadas de métodos de ORM.

Reflejo: saber leer un SELECT/JOIN incluso con un ORM.

Inyección SQL
Fallo crítico : una entrada concatenada se vuelve código ejecutado ; se protege con parametrización.

Ejemplo sencillo: Una comilla tramposa neutralizada por $1.

Lo que no es: un simple error de visualización sin gravedad.

Reflejo: no concatenar jamás, parametrizar siempre.

ORM (Prisma, Sequelize)
Mapea tablas ↔ objetos JS y genera el SQL : métodos legibles, seguros y productivos.

Ejemplo sencillo: prisma.client.findMany({ where }) en casa de Salim.

Lo que no es: SQL en bruto concatenado a mano.

Reflejo: privilegiar el ORM para el CRUD corriente.

Prisma (moderno)
ORM de esquema declarativo (schema.prisma), migraciones versionadas y cliente generado tipado.

Ejemplo sencillo: La migración que añade la tabla de líneas.

Lo que no es: un driver en bruto sin modelo ni tipo.

Reflejo: versionar cada evolución con una migración.

En la práctica — Persistir con una base SQL y un ORM

  1. Comprended el modelo relacional : tablas, columnas tipadas, filas, relaciones por claves foráneas ; y las operaciones SQL básicas (SELECT, INSERT, UPDATE, DELETE, JOIN).
  2. Retenede el imperativo de seguridad : no concatenar JAMÁS datos de usuario en SQL (inyección) — consultas parametrizadas u ORM.
  3. Descubrid un ORM moderno : definid un esquema (Prisma : schema.prisma) y haced un CRUD vía métodos (prisma.user.create/findMany/update/delete).
  4. Explorad las migraciones Prisma (versionar la evolución del esquema) y el interés del cliente tipado (autocompletado, seguridad de tipos con TypeScript).
Sabéis persistir con una base relacional (PostgreSQL/MySQL) vía un ORM moderno (Prisma), escribiendo consultas seguras — completando vuestro dominio de ambas familias de bases.

Guion listo para decir — presentar su SQL seguro (30 s)

« La facturación está en PostgreSQL : tablas clientes, pedidos y líneas vinculadas por claves foráneas. El CRUD pasa por Prisma, esquema declarativo más migraciones, cliente tipado. El SQL en bruto restante está parametrizado en $1, jamás concatenado : probado con una entrada tramposa, la inyección queda neutralizada. »

Para decir en revisión de seguridad ; mostrar el esquema y una consulta parametrizada.

Modelo copiar-pegar — Prisma + consulta parametrizada

// schema.prisma (extracto ficticio) :
// model Client { id Int @id @default(autoincrement())
//   nom String
//   email String @unique
//   commandes Commande[] }
async function main() {
  const nouveau = await prisma.client.create({
    data: { nom: 'Salim', email: 'salim@exemple-fictif.test' }
  });
  const liste = await prisma.client.findMany({ where: { actif: true } });
  await prisma.client.update({
    where: { email: 'salim@exemple-fictif.test' },
    data: { actif: false }
  });
  // Recordatorio bruto SEGURO (pg) — jamás concatenación :
  // await pool.query('SELECT * FROM clients WHERE email = $1', [emailSaisi]);
}
main().catch(console.error);

Independiente

Facturar en PostgreSQL + Prisma : tres tablas vinculadas y un CRUD tipado bastan.

Pyme en Argel

Salim versiona cada añadido de columna con una migración revisada antes del despliegue.

Equipo de producto

Imponer el cliente tipado compartido para consultas coherentes entre equipos.

Principiante

Esta semana : un modelo Prisma, un create y un findMany con where.

Prudencia: Datos y acceso SQL : trabajad sobre una base local o de prueba con datos ficticios (emails en @exemple-fictif.test), no peguéis jamás contraseña ni cadena de conexión real en el código o las capturas, y dad a cada rol el mínimo de derechos. Toda entrada de usuario va en parámetro ($1, ORM), jamás concatenada. En caso de duda — secreto expuesto o consulta concatenada — deteneos, purgad el secreto y regeneradlo antes de continuar.

✓ Buenas prácticas

  • Usar un ORM moderno (Prisma) para la productividad y la seguridad.
  • Siempre consultas parametrizadas (jamás concatenación) contra la inyección SQL.
  • Comprender el SQL subyacente y cargar las relaciones con eficacia (evitar el N+1).

✗ Errores frecuentes

  • Concatenar datos de usuario en una consulta SQL (inyección crítica).
  • Tratar el ORM como una caja negra (consultas ineficaces, N+1).
  • Ignorar las bases del SQL apoyándose por entero en el ORM.

Puntos clave

  • SQL / RELACIONAL (PostgreSQL, MySQL) : datos en TABLAS/FILAS con relaciones por CLAVES FORÁNEAS ; garantías fuertes (ACID).
  • Lenguaje SQL : SELECT/INSERT/UPDATE/DELETE + JOIN. Seguridad CRÍTICA : jamás concatenar datos de usuario (INYECCIÓN SQL).
  • Un ORM (PRISMA, Sequelize) mapea tablas ↔ objetos y genera el SQL : legible, seguro contra inyecciones, productivo.
  • PRISMA : esquema declarativo + migraciones + cliente TIPADO. Dominar SQL + NoSQL = caja de herramientas de persistencia completa.

Reto 48h — aplica mañana

Declarad 1 modelo Prisma y haced un create + findMany, luego reescribid un SELECT en parametrizado $1.

Lista de salida de la sección
  • Cito tablas, claves foráneas y ACID.
  • Mi SQL en bruto está parametrizado, jamás concatenado.
  • Mi CRUD Prisma pasa por esquema + migración.
Autoevaluación 1-5 : ¿están parametrizadas todas mis consultas en bruto?¿sé explicar el SQL generado por mi ORM?

Preguntas frecuentes

¿Driver SQL en bruto u ORM (Prisma/Sequelize) : qué elegir?

Para la mayoría de apps, un ORM : productividad (prisma.user.create() conciso), seguridad (parametrización automática anti-inyección), legibilidad y relaciones simples. El driver en bruto (pg) guarda su lugar para el control fino y las consultas precisas. Salim combina : ORM a diario, SQL parametrizado para los informes JOIN complejos.

¿Qué es la inyección SQL y cómo protegerse?

Una entrada de usuario concatenada en SQL puede volverse código ejecutado (elusión de auth, lectura o destrucción de datos). Ejemplo del peligro : ' OR '1'='1 deslizado en un email. Protección : consultas parametrizadas (placeholders $1 que separan código y datos) y ORM que parametrizan por defecto. Jamás concatenación, incluso en prueba — es un saber de seguridad fundamental.

Comprueba tu comprensión

¿Qué práctica protege de una inyección SQL en Node?

Recursos adicionales (opcional, para ir más allá)

Examen del nivel 3Preguntas inéditas y más difíciles para validar tus conocimientos