Diseñar un Schema con Relaciones y Validaciones

Lectura
20 min~4 min lectura

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.