Concepto clave
La optimización de queries en Prisma se centra en reducir el tiempo de respuesta y el consumo de recursos al interactuar con la base de datos. En producción, cada milisegundo cuenta, y una query mal diseñada puede ralentizar toda la aplicación. Piensa en una biblioteca: si buscas un libro específico, es más rápido ir directamente a la sección correcta que revisar todos los estantes. En Prisma, esto se traduce en usar select para traer solo los campos necesarios, where con índices adecuados, y evitar N+1 queries que generan múltiples consultas innecesarias.
Un concepto fundamental es el plan de ejecución de la base de datos, que determina cómo se accede a los datos. Prisma, al ser un ORM, abstrae esto, pero debes entender cómo tus decisiones afectan el rendimiento. Por ejemplo, usar includes para relaciones puede ser eficiente, pero si traes datos de tablas grandes sin filtrar, puede saturar la memoria. La clave es equilibrar simplicidad del código con eficiencia, priorizando queries que escalen bien con el crecimiento de los datos.
Cómo funciona en la práctica
Imagina que desarrollas una app de e-commerce con Prisma y necesitas mostrar los pedidos de un usuario con sus productos. Un enfoque inicial podría ser: primero, consultar los pedidos del usuario, luego, para cada pedido, consultar los productos asociados. Esto genera múltiples queries (una por pedido), lo que es ineficiente. En cambio, debes usar una sola query con include para traer todo en una sola operación.
Paso a paso: 1) Define tu schema en Prisma con relaciones claras (ej., User → Order → Product). 2) En tu código, escribe una query que use include anidado para obtener usuario, pedidos y productos en una sola consulta. 3) Aplica filtros con where para limitar los datos (ej., solo pedidos del último mes). 4) Usa select para especificar solo los campos necesarios, evitando traer columnas pesadas como descripciones largas. Esto reduce la transferencia de datos y mejora el rendimiento.
Codigo en accion
Aquí un ejemplo de una query no optimizada (antes) y optimizada (después) para una app de blog:
// ANTES: Query no optimizada (N+1 problem)
async function getPostsWithComments(userId) {
const posts = await prisma.post.findMany({
where: { authorId: userId },
});
// Esto genera una query por cada post
for (const post of posts) {
post.comments = await prisma.comment.findMany({
where: { postId: post.id },
});
}
return posts;
}// DESPUES: Query optimizada con include y select
async function getPostsWithCommentsOptimized(userId) {
const posts = await prisma.post.findMany({
where: { authorId: userId },
include: {
comments: {
select: {
id: true,
content: true,
createdAt: true,
},
where: { isDeleted: false }, // Filtro adicional
},
},
select: {
id: true,
title: true,
publishedAt: true,
comments: true, // Incluye solo los campos seleccionados arriba
},
orderBy: { publishedAt: 'desc' },
take: 10, // Limita resultados
});
return posts;
}Errores comunes
- No usar índices en campos filtrados frecuentemente: Si buscas usuarios por email sin índice, la query será lenta. Solución: Agrega @@index([email]) en el schema de Prisma.
- Traer todos los campos con select: false: Usar select: false o omitir select trae todas las columnas, incluyendo BLOBs o textos largos. Solución: Siempre especifica select con los campos necesarios.
- Ignorar el paginado en queries grandes: Consultar miles de registros de una vez puede sobrecargar la memoria. Solución: Usa skip y take para paginación.
- Abusar de includes anidados en relaciones complejas: Incluir muchas relaciones profundas puede generar joins costosos. Solución: Limita la profundidad o usa queries separadas cuando sea necesario.
- No monitorear el rendimiento en producción: Sin métricas, no sabes qué queries son lentas. Solución: Usa logs de Prisma o herramientas como Prisma Metrics para identificar cuellos de botella.
Checklist de dominio
- ¿Usas select para traer solo los campos necesarios en cada query?
- ¿Aplicas filtros con where en campos indexados para acelerar búsquedas?
- ¿Evitas el problema N+1 usando include o join en lugar de queries en bucle?
- ¿Implementas paginación con skip y take para datasets grandes?
- ¿Revisas y optimizas índices en tu schema basado en patrones de acceso comunes?
- ¿Monitoreas el rendimiento de queries en producción con herramientas adecuadas?
- ¿Refactorizas queries complejas en múltiples pasos si los includes son demasiado costosos?
Optimiza una Query de Reporte de Ventas
En este ejercicio, optimizarás una query existente en una app de ventas que genera reportes lentos. Sigue estos pasos:
- Contexto: Tienes un schema Prisma con modelos Sale, Product, y Customer. Sale tiene relaciones a Product y Customer.
- Query inicial: La función getSalesReport actual trae todas las ventas del último año, luego itera para obtener detalles de producto y cliente, causando N+1 queries.
- Paso 1: Refactoriza la query para usar include y traer producto y cliente en una sola operación.
- Paso 2: Aplica select para incluir solo campos clave: sale id, amount, date; product name; customer email.
- Paso 3: Agrega filtros con where para limitar a ventas activas (ej., status = 'completed') y ordena por fecha descendente.
- Paso 4: Implementa paginación con take: 50 para evitar sobrecarga.
- Paso 5: Escribe un test simple que verifique que la query optimizada devuelve los datos esperados.
Usa el siguiente código base para empezar:
// Código base (no optimizado)
async function getSalesReport() {
const sales = await prisma.sale.findMany({
where: { createdAt: { gte: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000) } },
});
for (const sale of sales) {
sale.product = await prisma.product.findUnique({ where: { id: sale.productId } });
sale.customer = await prisma.customer.findUnique({ where: { id: sale.customerId } });
}
return sales;
}
Pistas- Recuerda que include puede anidarse para relaciones como product y customer en el modelo Sale.
- Usa select dentro de include para limitar campos de las relaciones y mejorar rendimiento.
- Considera agregar un índice en createdAt en el schema si no existe, para acelerar el filtro por fecha.
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.