Saltar al contenido

Lección 2 de 17 · Lectura y práctica

Diseñar un Schema con Relaciones y Validaciones

Concepto claveDiseñar un schema en Prisma es como crear los planos de un edificio antes de construirlo. Define la estructura de tu base de datos, incluyendo tablas (modelos), columnas (campos) y cómo se conectan entre sí.

Del concepto a la práctica

Taller de esta lección

Una relación útil debe expresar tanto la dirección como la regla de propiedad. Para una agenda de salas, Reservation pertenece a un User y a una Room; sus claves foráneas no deben confundirse con una validación de fechas, que requiere lógica adicional.

Ejemplo resuelto

prisma
model Reservation {
  id        Int      @id @default(autoincrement())
  startsAt  DateTime @db.Timestamptz(3)
  endsAt    DateTime @db.Timestamptz(3)
  userId    Int
  roomId    Int
  user      User     @relation(fields: [userId], references: [id])
  room      Room     @relation(fields: [roomId], references: [id])
  @@index([roomId, startsAt])
}

El índice acompaña la consulta prevista por sala y comienzo.

Reservation es un fragmento: User yRoom deben existir con la relación inversa para validar el schema. Añade modelos mínimos:

prisma
model User {
  id Int @id
  name String
  reservations Reservation[]
}
model Room {
  id Int @id
  code String @unique
  name String
  capacity Int
  reservations Reservation[]
}

Para las lecciones de estados añade enum ReservationStatus con PENDING,CONFIRMED,CANCELLED, status ReservationStatus @default(PENDING) y externalRef String @unique en Reservation. Rechaza endsAt<=startsAt, no solo fechas invertidas. PostgreSQL con foreign keys impide referencias ausentes; relationMode prisma no ofrece la misma garantía de base de datos. El índice ayuda al acceso, sin impedir solapamientos por sí solo.

Pruébalo

  1. Enumera entidades y propietarios.

  2. Separa restricciones estructurales de reglas de negocio.

  3. Añade claves foráneas e índices de búsqueda.

  4. Prueba una reserva con fechas invertidas en la capa de aplicación.

Comprueba tu resultado

La base rechaza una sala inexistente y la aplicación rechaza endsAt anterior a startsAt.

Error frecuente: asumir que un tipo DateTime valida el orden entre dos campos. Corrígelo con una comprobación explícita antes de escribir.

Ejercicio: crea dos usuarios, una sala y una reserva; verifica que la relación devuelve exactamente un usuario y una sala.

Tablas de clientes y pedidos: INNER JOIN conserva coincidencias y LEFT JOIN conserva también a Luis sin pedidos.
CursaloMapa de práctica
Relacionar tablas sin perder filas

Una clave conecta cada pedido con su cliente. INNER JOIN devuelve las coincidencias y puede producir varias filas por cliente. LEFT JOIN conserva también los clientes sin pedido y representa los campos ausentes con NULL.

Ver la infografía en tamaño completo ↗

Concepto clave

Diseñar un schema en Prisma es como crear los planos de un edificio antes de construirlo. Define la estructura de tu base de datos, incluyendo tablas (modelos), columnas (campos) y cómo se conectan entre sí (relaciones). En producción, un buen schema no solo organiza los datos, sino que también previene errores mediante validaciones y optimiza el rendimiento desde el inicio.

Imagina que estás diseñando una plataforma de e-commerce. Necesitas modelos para Usuario, Producto y Pedido. Las relaciones entre ellos (un usuario puede tener muchos pedidos, un pedido puede contener varios productos) son cruciales para consultas eficientes. Las validaciones aseguran que, por ejemplo, el precio de un producto nunca sea negativo o que un email tenga formato correcto, evitando datos corruptos que podrían romper tu aplicación en producción.

Cómo funciona en la práctica

Primero, defines tus modelos en el archivo schema.prisma. Cada modelo se traduce a una tabla en la base de datos. Para relaciones, usas campos especiales que conectan modelos, como @relation. Las validaciones se aplican directamente en los campos con atributos como @unique, @default, o restricciones de tipo.

Paso a paso: 1) Identifica las entidades principales de tu aplicación (ej: Usuario, Producto). 2) Define los campos de cada modelo con tipos de datos precisos (ej: String, Int, DateTime). 3) Establece relaciones usando claves foráneas implícitas o explícitas. 4) Añade validaciones para integridad de datos. 5) Ejecuta prisma migrate dev para generar migraciones que aplican el schema a la base de datos.

Código en acción

Aquí un ejemplo funcional para un sistema de blog con usuarios y posts, incluyendo relaciones y validaciones básicas:

// schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String   @db.VarChar(200)
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
  createdAt DateTime @default(now())
}

Ahora, mejorémoslo para producción añadiendo validaciones avanzadas y una relación muchos-a-muchos para etiquetas:

// schema.prisma mejorado
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique @db.VarChar(255)
  name      String?  @db.VarChar(100)
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@map("users") // Nombre personalizado de tabla
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String   @db.VarChar(200)
  content   String?  @db.Text
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id], onDelete: Cascade)
  authorId  Int
  tags      Tag[]
  createdAt DateTime @default(now())

  @@map("posts")
}

model Tag {
  id    Int    @id @default(autoincrement())
  name  String @unique @db.VarChar(50)
  posts Post[]

  @@map("tags")
}

Errores comunes

  • No definir relaciones correctamente: Olvidar el campo authorId en Post o no usar @relation causa errores en migraciones. Siempre incluye ambos lados de la relación.
  • Validaciones insuficientes: Usar String sin límites (como @db.VarChar(255)) puede llevar a datos excesivamente largos en producción. Especifica restricciones de base de datos.
  • Ignorar eliminación en cascada: En relaciones, no configurar onDelete (ej: Cascade o SetNull) puede dejar datos huérfanos. Define el comportamiento según tu lógica de negocio.
  • No usar migraciones incrementalmente Hacer cambios grandes en el schema de una vez complica el rollback. Aplica migraciones pequeñas y frecuentes.
  • Olvidar índices para rendimiento: En producción, campos usados frecuentemente en consultas (como authorId) deben tener índices. Añade @@index en modelos.

Checklist de dominio

  1. ¿Definiste todos los modelos necesarios para tu aplicación con campos tipados correctamente?
  2. ¿Estableciste relaciones (uno-a-muchos, muchos-a-muchos) usando @relation y campos de clave foránea?
  3. ¿Añadiste validaciones clave como @unique, @default, y restricciones de base de datos (ej: @db.VarChar(255))?
  4. ¿Configuraste comportamientos de eliminación (ej: onDelete: Cascade) para mantener la integridad referencial?
  5. ¿Usaste @@map para personalizar nombres de tablas si es necesario para convenciones de tu equipo?
  6. ¿Consideraste añadir índices con @@index para campos consultados frecuentemente?
  7. ¿Probaste el schema generando una migración con prisma migrate dev y verificando que no haya errores?

Diseña un Schema para un Sistema de Reservas de Hotel

En este ejercicio, crearás un schema Prisma para un sistema de reservas de hotel en producción. Sigue estos pasos:

  1. Crea un nuevo proyecto Node.js e instala Prisma con npm install prisma @prisma/client.
  2. Inicializa Prisma con PostgreSQL: npx prisma init.
  3. En schema.prisma, define los siguientes modelos con relaciones y validaciones:
    • Habitacion: id, numero (único), tipo (ej: 'individual', 'doble'), precioPorNoche, reservas (relación).
    • Cliente: id, email (único), nombre, telefono, reservas (relación).
    • Reserva: id, fechaEntrada, fechaSalida, total, cliente (relación), habitacion (relación).
  4. Añade validaciones: precioPorNoche debe ser positivo, email con formato, fechas válidas.
  5. Configura la relación entre Reserva y Habitacion como muchos-a-uno, y entre Reserva y Cliente como muchos-a-uno.
  6. Ejecuta npx prisma migrate dev --name init para generar y aplicar la migración.
  7. Verifica que no haya errores y que las tablas se creen correctamente en tu base de datos.
Pistas
  • Usa tipos como @db.Decimal para precioPorNoche para precisión en moneda.
  • Considera añadir un campo @default(now()) para fecha de creación en Reserva.
  • Para la relación, asegúrate de incluir habitacionId y clienteId en el modelo Reserva.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.

Sigue con Prisma ORM Profesional: Schemas, Migraciones y Queries Optimizadas para Producción

Esta lección forma parte de un curso de Cursalo. Revisa su programa y elige entre la compra individual o el acceso a toda la escuela.

Si ya tenías acceso a este curso, entra con tu cuenta: tus derechos y tu progreso se conservan. No necesitas comprarlo de nuevo.

01 / ELEGIR UNA RUTA

El próximo paso

Aprende IA mientras construyes algo que sí necesitas.

Empieza por el curso inicial o explora el catálogo para encontrar una práctica alineada con tu trabajo.