Concepto clave
El quiz final de este proyecto integrador evalúa tu capacidad para aplicar Prisma ORM en un entorno de producción real. No se trata solo de recordar sintaxis, sino de demostrar que puedes diseñar schemas escalables, ejecutar migraciones seguras y escribir queries optimizadas que funcionen bajo carga. Piensa en esto como el examen final de un piloto: no basta con conocer los controles, debes demostrar que puedes aterrizar el avión en condiciones adversas.
En producción, cada decisión tiene consecuencias. Un schema mal diseñado puede limitar el crecimiento de tu aplicación. Una migración ejecutada sin pruebas puede causar downtime. Una query ineficiente puede saturar tu base de datos. Este quiz simula esas situaciones críticas donde tu conocimiento teórico debe convertirse en acción práctica.
Cómo funciona en la práctica
Imagina que estás desarrollando una API para un e-commerce con 100,000 usuarios activos. Tu tarea es implementar un sistema de pedidos que incluya: usuarios, productos, pedidos y detalles de pedido. Primero, diseñas el schema en Prisma, considerando relaciones, índices y tipos de datos apropiados. Luego, creas y aplicas migraciones sin interrumpir el servicio existente. Finalmente, optimizas las queries para que la página de historial de pedidos cargue en menos de 200ms incluso con miles de registros.
Paso a paso: 1) Analiza los requisitos del negocio y modela las entidades. 2) Define las relaciones (1-a-1, 1-a-muchos, muchos-a-muchos) en el schema. 3) Aplica migraciones en etapas (usando herramientas como prisma migrate dev y prisma migrate deploy). 4) Escribe queries que usen select, include, y where de forma eficiente. 5) Prueba el rendimiento con datos realistas.
Código en acción
Ejemplo de un schema optimizado para producción:
// schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Usuario {
id Int @id @default(autoincrement())
email String @unique
nombre String
pedidos Pedido[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email]) // Índice para búsquedas rápidas
}
model Producto {
id Int @id @default(autoincrement())
nombre String
precio Decimal @db.Decimal(10, 2)
stock Int @default(0)
detalles DetallePedido[]
createdAt DateTime @default(now())
}
model Pedido {
id Int @id @default(autoincrement())
usuarioId Int
usuario Usuario @relation(fields: [usuarioId], references: [id])
detalles DetallePedido[]
total Decimal @db.Decimal(10, 2)
estado String @default("pendiente")
createdAt DateTime @default(now())
@@index([usuarioId]) // Mejora rendimiento en joins
}
model DetallePedido {
id Int @id @default(autoincrement())
pedidoId Int
productoId Int
pedido Pedido @relation(fields: [pedidoId], references: [id])
producto Producto @relation(fields: [productoId], references: [id])
cantidad Int
precioUnitario Decimal @db.Decimal(10, 2)
@@unique([pedidoId, productoId]) // Evita duplicados
}Query optimizada para obtener el historial de pedidos de un usuario:
// ANTES: Query ineficiente que carga datos innecesarios
const pedidos = await prisma.pedido.findMany({
where: { usuarioId: 123 },
include: {
detalles: {
include: { producto: true }
}
}
});
// DESPUÉS: Query optimizada con select específico
const pedidosOptimizados = await prisma.pedido.findMany({
where: { usuarioId: 123 },
select: {
id: true,
total: true,
estado: true,
createdAt: true,
detalles: {
select: {
cantidad: true,
precioUnitario: true,
producto: {
select: {
nombre: true,
precio: true
}
}
}
}
},
orderBy: { createdAt: 'desc' },
take: 10 // Paginación para evitar cargar miles de registros
});Errores comunes
- No usar índices en campos de búsqueda frecuente: En tablas grandes, buscar por email sin índice puede ser 100x más lento. Solución: Agrega
@@index([email])en el modelo. - Migraciones sin respaldo en producción: Ejecutar
prisma migrate deploysin haber probado en staging puede corromper datos. Solución: Siempre prueba migraciones en un entorno idéntico a producción primero. - Queries que cargan relaciones completas: Usar
includesinselectcarga todos los campos, incluso los no necesarios. Solución: Especifica solo los campos necesarios conselect. - Olvidar paginación en listados largos: Un
findMany()sintakeoskippuede traer millones de registros. Solución: Implementa paginación contakeyskip. - No manejar transacciones en operaciones críticas: Crear un pedido sin transacción puede dejar datos inconsistentes si falla un paso. Solución: Usa
prisma.$transaction()para operaciones multi-paso.
Checklist de dominio
- ¿Puedes diseñar un schema Prisma que modele relaciones complejas (muchos-a-muchos, anidadas) correctamente?
- ¿Sabes ejecutar migraciones en producción sin downtime usando estrategias como migraciones progresivas?
- ¿Escribes queries que usen
selecten lugar deincludecuando solo necesitas campos específicos? - ¿Implementas paginación en todas las queries que pueden devolver muchos resultados?
- ¿Usas índices en campos de búsqueda frecuente y claves foráneas?
- ¿Manejas transacciones para operaciones que deben ser atómicas (ej: crear pedido con múltiples items)?
- ¿Validas datos de entrada antes de pasarlos a Prisma para prevenir inyecciones o errores de tipo?
Optimización de API de Pedidos para Escala
Tu equipo necesita optimizar una API existente que está teniendo problemas de rendimiento bajo carga. La API maneja pedidos para un e-commerce y actualmente tarda más de 2 segundos en cargar el historial de pedidos de usuarios con muchos registros.
- Diagnostica el problema: Examina el siguiente código actual y identifica al menos 3 problemas de rendimiento:
- Rediseña el schema: Si fuera necesario, propone mejoras al schema Prisma para optimizar las consultas. Considera índices, tipos de datos y relaciones.
- Reescribe la query: Crea una versión optimizada de la función
getHistorialUsuarioque:- Use
selecten lugar deincludepara cargar solo campos necesarios - Implemente paginación (limite de 20 pedidos por página)
- Incluya un orden descendente por fecha de creación
- Maneje errores adecuadamente
- Use
- Propón una migración: Si agregaste índices en el paso 2, escribe el comando Prisma para crear y aplicar la migración de forma segura.
// Código actual (problemático)
async function getHistorialUsuario(usuarioId: number) {
return await prisma.pedido.findMany({
where: { usuarioId },
include: {
usuario: true, // Carga todos los campos del usuario
detalles: {
include: {
producto: true // Carga todos los campos del producto
}
}
}
});
}- Revisa qué campos son realmente necesarios para mostrar el historial de pedidos. ¿Necesitas todos los campos de usuario y producto?
- Recuerda que cada
includesinselectcarga TODOS los campos de la relación, lo que puede ser muy costoso. - Para paginación, investiga los parámetros
takeyskipen la documentación de Prisma.
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.