Frontend nextjs

Por qué cachear resultados de búsqueda en Next.js es mala idea

Es tentador envolver tu función de búsqueda en unstable_cache para 'ganar performance'. En la práctica, casi siempre hace más daño que bien. Aquí por qué.

7 min
Next.js Caching SSR Performance

Después de configurar unstable_cache en el catálogo y ver la mejora de performance, la tentación natural es aplicar lo mismo a todo, incluyendo la búsqueda: “si cacheo el catálogo, ¿por qué no cachear también los resultados de búsqueda?”.

La respuesta corta: porque el patrón de acceso es completamente distinto, y forzar cache ahí puede terminar costándote más de lo que ahorra — o peor, sirviendo resultados desactualizados de forma silenciosa. Te explico el razonamiento completo, que conecta directo con lo que vimos en los 3 mecanismos de cache en Next.js.

La pregunta que determina si algo vale la pena cachear

No es “¿esto tarda en calcularse?”. Es: ¿cuántas veces se va a repetir exactamente la misma consulta?

  • La home de un catálogo: miles de usuarios distintos piden exactamente lo mismo → altísimo hit rate, cachear es una decisión obvia.
  • Una búsqueda de texto libre: cada usuario escribe algo distinto (“mouse”, “teclado rgb”, “monitor 4k baratos”) → el hit rate real es bajísimo, salvo unos pocos términos muy populares.

Cachear algo con bajo hit rate no solo no ahorra mucho — además ocupa espacio en tu Data Cache con entradas que casi nunca se van a reutilizar, y complica tu lógica de invalidación sin beneficio real.

El problema de fondo: cache y frescura compiten

Hay una segunda razón, más sutil que la de performance: los resultados de búsqueda son, por naturaleza, más sensibles a estar actualizados que un catálogo general. Si alguien busca “en stock” o filtra por disponibilidad, y le muestras un resultado cacheado de hace 20 minutos, puede llevarlo a comprar algo que ya no existe.

// La función de búsqueda, deliberadamente SIN cache
export async function searchProducts(query: string) {
  console.log(`🔴 QUERY REAL: searchProducts("${query}")`)
  return prisma.product.findMany({
    where: {
      OR: [
        { name: { contains: query } },
        { description: { contains: query } },
        { category: { contains: query } },
      ],
    },
    orderBy: { name: 'asc' },
  })
}

Nota lo que no hay ahí: nada de unstable_cache, nada de React.cache. Cada búsqueda ejecuta una query fresca, siempre.

La ruta también debe declararse explícitamente dinámica

// src/app/buscar/page.tsx
export const dynamic = 'force-dynamic'

export default async function SearchPage({
  searchParams,
}: {
  searchParams: Promise<{ q?: string }>
}) {
  const { q } = await searchParams
  const results = q ? await searchProducts(q) : []
  // ...
}

force-dynamic es una declaración explícita: “esta ruta nunca debe pasar por el Full Route Cache”. Aunque técnicamente Next.js suele detectar automáticamente que una ruta es dinámica cuando usa searchParams, dejarlo explícito documenta la intención — cualquiera que abra el archivo entiende de inmediato por qué esta ruta se comporta distinto al resto del sitio, sin tener que rastrear la cadena de llamadas.

Cómo comprobar que realmente no se está cacheando nada

Busca lo mismo dos veces seguidas, sin cambiar el término. Si tu configuración es correcta, deberías ver el log 🔴 ambas veces en tu terminal — a diferencia de una ruta con ISR, donde después de la primera visita el log desaparece por completo. Esa comparación directa es la prueba de que entendiste bien la diferencia entre una ruta cacheada y una verdaderamente dinámica.

Cuándo SÍ vale la pena cachear búsquedas (la excepción)

No es una regla absoluta. Si tu aplicación tiene un patrón de búsqueda con alta concentración en pocos términos —por ejemplo, un sitio de noticias donde el 80% de las búsquedas son sobre 5 temas de tendencia— ahí sí puede tener sentido cachear con un revalidate corto (segundos, no horas) y un tag específico por término normalizado. Pero eso es la excepción que confirma la regla: se justifica con datos reales de patrón de uso, no por “cachear todo lo que se pueda”.

Reglas prácticas

  1. Mide el hit rate esperado antes de cachear. Si cada consulta es probablemente única, cachear no te ahorra casi nada.
  2. Prioriza frescura sobre velocidad en búsqueda. El usuario que busca espera resultados actuales, no una foto de hace un rato.
  3. Declara force-dynamic explícitamente en rutas que dependen de searchParams arbitrarios, aunque Next.js ya lo detecte solo — es documentación viva para el resto del equipo.
  4. No cachees “por si acaso”. Cachear sin un patrón de reutilización real es complejidad sin beneficio.

Conclusión

No todo merece cache. La búsqueda es el ejemplo perfecto de una ruta donde la variabilidad del input hace que cachear sea, en la práctica, casi lo mismo que no cachear — pero con el costo extra de gestionar invalidación y espacio de almacenamiento. A veces la decisión más inteligente sobre cache es simplemente no usarlo.

¿Tu aplicación está cacheando cosas que en realidad no lo necesitan (o al revés)? Agenda una auditoría técnica gratuita de 15 minutos y revisamos juntos tu estrategia de cache.

¿Tienes un proyecto en mente?

Convierte tu idea en un producto real

Desarrollo web, aplicaciones a medida y consultoría tecnológica para empresas y startups. Cuéntame tu proyecto y te respondo en menos de 24 horas.

Solicitar presupuesto Ver LinkedIn