Inicio / Artículos / Comprensión de la directiva “use cache” y la revalidación basada en etiquetas de Next.js 16

Comprensión de la directiva “use cache” y la revalidación basada en etiquetas de Next.js 16

Aprenda cómo funciona la directiva “use cache” en Next.js 16, sus funciones de revalidación asociadas, y cómo aplicar un caché consciente del inquilino en aplicaciones multiinquilinos.

1215 palabras

El caché en Next.js tradicionalmente ha parecido algo como una caja negra: un mosaico de configuraciones a nivel de archivo, opciones pasadas a fetch, y valores predeterminados del framework que cambiaban lo suficiente entre versiones como para que incluso los desarrolladores experimentados mantuvieran la documentación abierta por seguridad. La directiva "use cache" introducida en Next.js 16, como parte del modelo más amplio de Componentes de Caché, cambia esto al permitir declarar explícitamente el comportamiento de caché a nivel de un componente o función, en lugar de heredarlo implícitamente de valores predeterminados que se supone deben recordarse.

A continuación se detalla qué hace realmente la directiva y las situaciones en las que tiene sentido utilizarla.

Qué hace realmente la directiva

Al colocar "use cache" dentro de una función o componente, se indica a Next.js que almacene en caché todo lo que devuelva esa función. Conceptualmente, su función es similar a la de "use client", excepto que en lugar de marcar un límite del lado del cliente, marca un límite para el caché:

async function getDashboardStats(tenantId: string) {
  "use cache";
  const stats = await db.query.stats.findMany({ where: { tenantId } });
  return stats;
}

La salida de la función se almacena en caché y se identifica según los argumentos proporcionados, para luego reutilizarse en solicitudes posteriores hasta que algo la invalide. Esto representa un cambio significativo respecto al enfoque anterior de cachear una llamada fetch individual o todo un segmento de ruta: ahora se puede cachear en el nivel de granularidad que mejor se adapte a los datos, incluso hasta en el nivel de una función específica.

Las tres funciones auxiliares

Junto con esta directiva, Cache Components introduce un pequeño conjunto de APIs para gestionar intencionalmente los datos en caché, en lugar de esperar simplemente a que expire un temporizador:

  • revalidateTag(tag) — elimina todas las entradas de caché asociadas a una etiqueta determinada. Es útil cuando una sola mutación afecta datos en los que dependen varias funciones de caché diferentes.
  • updateTag(tag) — una versión más específica de la misma idea, destinada a actualizar solo las entradas de caché relacionadas con una etiqueta concreta y no todo lo que esté bajo ella.
  • refresh() — actualiza los datos de caché relacionados con la solicitud actual.

Aquí está el patrón que hace que todo el sistema funcione correctamente: asignar una etiqueta significativa a cada función en caché, algo como tenant-stats o invoice-list, y cada vez que ocurra una mutación que afecte esos datos subyacentes, llamar a revalidateTag con la etiqueta correspondiente en lugar de intentar establecer un período de vencimiento basado en el tiempo que sea demasiado corto (lo cual anula la finalidad del caché) o demasiado largo (lo que genera el uso de datos obsoletos).

async function getInvoices(tenantId: string) {
  "use cache";
  cacheTag(`invoices-${tenantId}`);
  return db.query.invoices.findMany({ where: { tenantId } });
}
// After creating an invoice:
async function createInvoice(data: InvoiceInput) {
  await db.insert(invoices).values(data);
  revalidateTag(`invoices-${data.tenantId}`);
}

Esa combinación —etiquetar en la lectura y volver a validar en la escritura— representa realmente toda la idea en su esencia. Casi todo lo demás que se haga con este sistema es una variación de esa misma combinación.

Puntos comunes de confusión

El error más común entre los equipos es asumir que "use cache" es un sustituto sencillo de la opción next: { revalidate } en fetch(). En realidad, abordan problemas diferentes, aunque relacionados. La opción a nivel de fetch gestiona una única solicitud de red. "use cache", por su parte, almacena en caché el resultado de toda una función o componente, la cual podría realizar muchas más acciones internamente: consultar una base de datos, ejecutar cálculos o incluso llamar a fetch en algún momento del proceso. Si lo único que necesitas es almacenar en caché una única llamada a una API externa, optar por el caché a nivel de fetch suele ser la elección más sencilla. "use cache" resulta útil cuando deseas guardar en caché todo el resultado de un cálculo, y no solo una sola solicitud que lo genera.

El segundo error frecuente es omitir por completo el paso de etiquetado, lo que genera confusión posterior cuando una mutación no logra eliminar los datos en caché que debería. Sin una etiqueta asociada, el único mecanismo de invalidación disponible es el tiempo, lo cual socava gran parte de la razón por la que se adoptó este modelo en primer lugar: has asumido la complejidad adicional del caché explícito sin obtener un control explícito sobre su invalidación.

Etiquetas conscientes del inquilino para aplicaciones multiinquilino

Si está trabajando en algo multiinquilino, hay un detalle importante que merece ser señalado directamente: las etiquetas deben codificar al inquilino, no solo el tipo de datos que se están almacenando en caché. Una etiqueta genérica como invoices compartida entre todos los inquilinos significa que al invalidar los datos de un inquilino, también se invalidan para todos; esto puede generar problemas de corrección (un inquilino ve resultados obsoletos porque una mutación en otro inquilino desencadenó una revalidación compartida) o problemas de rendimiento (el caché se vacía con mucha más frecuencia de la necesaria). Algo como invoices-${tenantId}, tal como se usa en el ejemplo anterior, no es una preferencia estilística: es lo que distingue a una estrategia de invalidación funcional de una defectuosa.

Por qué vale la pena configurarlo correctamente desde el principio

Una capa de caché con un defecto sutil rara vez falla de manera evidente. En su lugar, tiende a manifestarse como tickets de soporte vagos del tipo “¿por qué este panel sigue mostrando las cifras de la semana pasada?”; errores que resultan realmente difíciles de rastrear, ya que la ruta de invalidación defectuosa suele encontrarse en un lugar al que nadie ha accedido en meses. Establecer desde el principio una estrategia de etiquetado coherente, aplicada de manera uniforme en todas las funciones en caché del código, es una de esas decisiones de infraestructura que cuestan poco implementar correctamente al inicio, pero resultan mucho más costosas de corregir posteriormente.

Algunos kits de inicio para paneles de control construyen su capa de datos basándose exactamente en esta disciplina. Como ejemplo, una plantilla como la plantilla de panel de control de Ovyqen para proyectos Next.js y SaaS aplica constantemente la combinación de etiquetas al leer y volver a validar al escribir, integrando los identificadores de usuarios desde el principio en cada etiqueta en lugar de aplicarlas posteriormente tras un error de caché entre usuarios. Si está evaluando plantillas de panel de control y la invalidación de caché es el aspecto de su aplicación actual en el que nadie confía del todo, ese es un motivo razonable para partir de una base que ya gestione esto correctamente.

Preguntas frecuentes

¿Debería migrarse cada llamada a fetch() a "use cache"? No necesariamente: estas dos herramientas se superponen pero no son intercambiables. El caché a nivel de fetch() sigue siendo adecuado para escenarios simples con una sola solicitud. Utilice "use cache" cuando necesite almacenar en caché un resultado calculado o la salida de todo un componente.

¿Está "use cache" lo suficientemente maduro para paneles de control en producción? Trátelo de la misma manera que cualquier mecanismo de caché relativamente nuevo: pruebe a fondo sus vías de invalidación, especialmente para datos multiinquilino, antes de confiar en él para cualquier elemento orientado al cliente donde sea importante que la información no esté desactualizada.

¿Qué sucede si una función en caché no se etiqueta? La caché sigue funcionando, pero pierdes la posibilidad de invalidarla deliberadamente en respuesta a un evento específico. Te quedas dependiendo únicamente de la expiración basada en el tiempo, lo cual rara vez es el comportamiento que realmente deseas.

La caché explícita requiere más esfuerzo inicial que confiar simplemente en los valores predeterminados de un framework. Pero para datos de paneles de control donde mostrar información obsoleta conlleva un costo real, ese esfuerzo adicional es casi siempre la mejor opción a tomar.

Lecturas relacionadas

  • 20 patrones avanzados de Next.js para aplicaciones con App Router de nivel producción — Aprenda veinte patrones de alto nivel para Next.js que abarcan el diseño basado en servidor, la transmisión en flujo continuo, el caché, el enrutamiento y el rendimiento, con el fin de crear aplicaciones de producción más rápidas y escalables.
  • Cómo evolucionó la ingeniería frontend de lo estilizado a los sistemas de escalado — Explora el cambio desde HTML/CSS/JS básicos hasta la arquitectura de componentes, el caché, los monorepos y la capacidad de observabilidad necesarios para atender a millones de usuarios de manera fiable.