Concepto clave
El problema N+1 queries es uno de los principales enemigos del rendimiento en aplicaciones que usan ORMs como Prisma. Ocurre cuando, para recuperar una colección de registros y sus relaciones, se ejecuta una consulta inicial para obtener los registros principales (1) y luego una consulta adicional por cada registro para obtener sus relaciones (N). Imagina que tienes una tienda online: primero consultas 100 productos (1 query) y luego, para cada producto, consultas su categoría (100 queries más). Eso son 101 consultas a la base de datos cuando podrían ser solo 2 o 3.
La paginación, por otro lado, es una técnica esencial para manejar grandes volúmenes de datos sin sobrecargar la memoria o la red. En lugar de devolver miles de registros de golpe, divides los resultados en páginas manejables. Piensa en un libro: no lees todas las páginas a la vez, sino que avanzas de página en página. En producción, esto mejora los tiempos de respuesta y reduce el consumo de recursos.
Cómo funciona en la práctica
Para evitar N+1 queries, Prisma ofrece métodos como include y select que permiten cargar relaciones de forma eficiente en una sola consulta. Por ejemplo, si tienes un modelo User con muchos Post, en lugar de hacer un bucle que consulte los posts de cada usuario, puedes usar include para traerlos todos juntos.
La paginación en Prisma se implementa principalmente con skip y take. skip indica cuántos registros saltar desde el inicio, y take cuántos tomar después de eso. Para una página 2 con 10 elementos por página, saltas los primeros 10 y tomas los siguientes 10. También puedes usar cursor para paginación basada en un identificador único, que es más eficiente para grandes conjuntos de datos.
Código en acción
Antes: Ejemplo de N+1 queries (ineficiente)
// Obtener usuarios y luego sus posts (N+1 queries)
const users = await prisma.user.findMany();
for (const user of users) {
const posts = await prisma.post.findMany({
where: { userId: user.id }
});
console.log(user.name, posts.length);
}Después: Solución con include (eficiente)
// Obtener usuarios con sus posts en una sola query
const usersWithPosts = await prisma.user.findMany({
include: {
posts: true
}
});
for (const user of usersWithPosts) {
console.log(user.name, user.posts.length);
}Ejemplo de paginación con skip y take:
// Paginación: página 2, 10 elementos por página
const page = 2;
const pageSize = 10;
const posts = await prisma.post.findMany({
skip: (page - 1) * pageSize, // Salta los primeros 10
take: pageSize, // Toma 10
orderBy: { createdAt: 'desc' }
});Errores comunes
- No usar include o select para relaciones: Caer en el patrón N+1 por olvidar cargar relaciones de forma eficiente. Solución: Siempre revisar si necesitas datos relacionados y usar
includeoselect. - Paginación sin orden: Paginar sin
orderBypuede dar resultados inconsistentes. Solución: Definir un orden claro (ej., por ID o fecha). - Take demasiado alto: Usar
takecon valores muy grandes (ej., 1000) anula los beneficios de la paginación. Solución: Limitar a un tamaño razonable (ej., 10-100). - Ignorar el conteo total: Para interfaces de usuario, a menudo necesitas el total de páginas. Solución: Usar
prisma.post.count()junto con la paginación. - No usar cursor para grandes datasets:
skippuede ser lento con millones de registros. Solución: Para paginación eficiente, considerarcursorbasado en un campo único.
Checklist de dominio
- Identificar y eliminar N+1 queries en tu código usando
includeoselect. - Implementar paginación con
skipytakeen todas las consultas que devuelvan listas grandes. - Usar
orderByen consultas paginadas para garantizar consistencia. - Probar el rendimiento con herramientas como Prisma Metrics o logs de queries.
- Considerar paginación basada en cursor para datasets muy grandes.
- Incluir conteo total cuando sea necesario para la interfaz de usuario.
- Revisar y ajustar el tamaño de página (
take) según el caso de uso.
Optimizar una API de blog con Prisma
En este ejercicio, refactorizarás una API de blog que sufre de N+1 queries y falta de paginación. Sigue estos pasos:
- Clona el repositorio base (simulado) que contiene un proyecto Node.js con Prisma y modelos
User,PostyComment. - Abre el archivo
src/routes/posts.jsy encuentra la ruta GET/postsque devuelve todos los posts con sus autores y comentarios, pero actualmente usa N+1 queries. - Refactoriza la consulta usando
includepara cargar autores y comentarios en una sola query. - Agrega paginación a la ruta: acepta parámetros de query
pageylimit(por defecto page=1, limit=10) y usaskipytake. - Modifica la respuesta para incluir metadatos de paginación:
totalPosts,totalPages,currentPage. - Prueba la API con herramientas como Postman o curl, verificando que las queries se reducen y la paginación funciona.
- Usa prisma.post.count() para obtener el total de posts antes de paginar.
- Recuerda que include puede anidarse: include: { author: true, comments: true }.
- Valida los parámetros page y limit para evitar valores negativos o demasiado altos.
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.