Caching Avanzado
Una vez que entiendes "use cache" (ver Data Fetching), el siguiente paso es controlar cuando y como se actualiza ese cache.
Revalidacion por tiempo
Usa cacheLife para definir cuanto tiempo dura el cache:
import { cacheLife } from "next/cache"
async function getProductos() {
"use cache"
cacheLife("hours") // Cache por 1 hora
const res = await fetch("https://api.example.com/productos")
return res.json()
}
Perfiles disponibles:
| Perfil | Duracion |
|---|---|
"seconds" | 1 segundo |
"minutes" | 5 minutos |
"hours" | 1 hora |
"days" | 1 dia |
"weeks" | 1 semana |
"max" | El maximo posible |
Revalidacion on-demand
Para invalidar el cache cuando algo cambia (despues de una mutation):
revalidatePath
Revalida todas las funciones con "use cache" que se usan en esa ruta:
"use server"
import { revalidatePath } from "next/cache"
export async function actualizarProducto(id: string, data: ProductoData) {
await db.producto.update({ where: { id }, data })
// Revalida la pagina de productos
revalidatePath("/productos")
// Revalida la pagina especifica del producto
revalidatePath(`/productos/${id}`)
}
revalidateTag
Mas preciso. Revalida solo las funciones marcadas con un tag especifico:
import { cacheTag } from "next/cache"
async function getProductos() {
"use cache"
cacheTag("productos")
return await db.producto.findMany()
}
async function getProducto(id: string) {
"use cache"
cacheTag("productos", `producto-${id}`)
return await db.producto.findUnique({ where: { id } })
}
"use server"
import { revalidateTag } from "next/cache"
export async function actualizarProducto(id: string, data: ProductoData) {
await db.producto.update({ where: { id }, data })
// Solo revalida lo que tenga el tag "productos"
revalidateTag("productos")
// O revalida solo un producto especifico
revalidateTag(`producto-${id}`)
}
revalidatePath vs revalidateTag
Usa revalidatePath cuando quieres refrescar toda una pagina. Usa revalidateTag cuando quieres ser mas preciso sobre que datos refrescar.
Comparacion con v15
v16Cambio| Concepto | v15 | v16 |
|---|---|---|
| Cache manual | unstable_cache() | "use cache" |
| Cache en fetch | next: { revalidate: 60 } | "use cache" + cacheLife() |
| Tags de cache | next: { tags: ["key"] } | cacheTag("key") |
| Revalidacion | revalidateTag() / revalidatePath() | Igual (no cambio) |
Estrategias comunes
Datos que cambian poco (catalogo, CMS)
async function getArticulos() {
"use cache"
cacheLife("hours")
cacheTag("articulos")
return await db.articulo.findMany({
where: { publicado: true },
orderBy: { fecha: "desc" },
})
}
Se revalida automaticamente cada hora, o manualmente cuando publicas un articulo.
Datos que cambian seguido (notificaciones, chat)
No uses cache. Usa fetch con cache: "no-store" o simplemente no pongas "use cache":
async function getNotificaciones(userId: string) {
// Sin "use cache" = datos frescos en cada request
return await db.notificacion.findMany({
where: { userId, leida: false },
orderBy: { createdAt: "desc" },
})
}
ISR (Incremental Static Regeneration)
Para paginas estaticas que se actualizan periodicamente:
// app/blog/[slug]/page.tsx
export default async function BlogPost({
params,
}: {
params: Promise<{ slug: string }>
}) {
"use cache"
cacheLife("days")
const { slug } = await params
const post = await getPost(slug)
return <article>{/* ... */}</article>
}
export async function generateStaticParams() {
const posts = await getAllPosts()
return posts.map((p) => ({ slug: p.slug }))
}
La pagina se genera en build time, se sirve estaticamente, y se regenera cada dia.
Ejemplo: e-commerce con cache inteligente
// lib/data.ts
import { cacheTag, cacheLife } from "next/cache"
// Catalogo: cambia poco, cache largo
export async function getCategorias() {
"use cache"
cacheLife("days")
cacheTag("categorias")
return await db.categoria.findMany({ include: { _count: true } })
}
// Productos: cache medio, revalidar al editar
export async function getProductos(categoriaId?: string) {
"use cache"
cacheLife("hours")
cacheTag("productos")
return await db.producto.findMany({
where: categoriaId ? { categoriaId } : undefined,
include: { categoria: true },
})
}
// Precio y stock: siempre frescos (sin cache)
export async function getStock(productoId: string) {
return await db.inventario.findUnique({
where: { productoId },
select: { cantidad: true, precio: true },
})
}
// app/actions/admin.ts
"use server"
import { revalidateTag } from "next/cache"
export async function editarProducto(id: string, data: ProductoData) {
await db.producto.update({ where: { id }, data })
revalidateTag("productos")
}
export async function editarCategoria(id: string, data: CategoriaData) {
await db.categoria.update({ where: { id }, data })
revalidateTag("categorias")
revalidateTag("productos") // Los productos muestran la categoria
}