Capítulo 28 de 456

Caching (Previous Model)

Core Idea

Modelo de cache para projetos SEM cacheComponents habilitado: cache de fetch via opção cache, cache de funções não-fetch via unstable_cache, e controle por route segment config (dynamic, fetchCache, revalidate).

Key Concepts

  • fetch(url, { cache: 'force-cache' }): por padrão fetch não é cacheado; force-cache cacheia a requisição individual.
  • unstable_cache(fn, keyParts, { tags, revalidate }): cacheia funções async que não usam fetch (ORM, DB direto); segundo argumento é prefixo da chave de cache.
  • dynamic: route segment config ('auto' | 'force-dynamic' | 'error' | 'force-static') que controla o comportamento de renderização de toda a rota.
  • fetchCache: opção avançada que sobrescreve o cache default de todos os fetch de uma rota ('auto', 'default-cache', 'only-cache', 'force-cache', 'default-no-store', 'only-no-store', 'force-no-store').
  • revalidate (route segment config): false (padrão, cache indefinido) | 0 (sempre dinâmico) | number (segundos); o menor revalidate entre layouts/pages de uma rota domina a rota inteira.
  • revalidateTag/revalidatePath: invalidação on-demand chamadas em Server Action ou Route Handler.
  • React cache(): deduplica chamadas não-fetch (ORM/DB) dentro de um único render pass (fetch já é memoizado automaticamente).
  • Preload pattern: server-only + React cache + função preload() chamada antes de trabalho bloqueante, pra iniciar fetch cedo.

Code Examples

export const getCachedUser = unstable_cache(
  async (id: string) => db.select().from(users).where(eq(users.id, id)).then(r => r[0]),
  ['user'],
  { tags: ['user'], revalidate: 3600 }
)
  • O que demonstra: cache de query de banco (não-fetch) com tag para invalidação e revalidação temporal.
export const dynamic = 'force-dynamic'
// equivalente a: cache: 'no-store' em todo fetch + fetchCache = 'force-no-store'
  • O que demonstra: forçar renderização dinâmica por request em toda a rota.
export const getPost = cache(async (id: string) => {
  return db.query.posts.findFirst({ where: eq(posts.id, parseInt(id)) })
})

export const preload = (id: string) => { void getItem(id) }
// chamar preload(id) antes de await checkIsAvailable() para iniciar fetch cedo
  • O que demonstra: dedup + preload pattern para ORMs, evitando esperar sequencialmente por dados que podem carregar em paralelo.
import { revalidateTag } from 'next/cache'
export async function updateUser(id: string) {
  revalidateTag('user', 'max')
}
  • O que demonstra: invalidação on-demand após mutação em Server Action.

Reference Tables

Opção dynamicEfeito
'auto' (padrão)Cacheia o máximo possível sem forçar opt-in dinâmico
'force-dynamic'Renderiza por request; equivale a cache:'no-store' em todo fetch
'error'Força prerender; erro se algo usar API de request-time ou dado não cacheado
'force-static'Força prerender; cookies()/headers()/useSearchParams() retornam vazio
Opção revalidateEfeito
false (padrão)Cache indefinido (equivale a Infinity); fetch individual pode usar no-store/revalidate:0
0Sempre dinâmico; muda default de fetch sem cache para 'no-store'
number (segundos)Revalidação a cada N segundos; deve ser estaticamente analisável (600, não 60*10)

Anti-patterns

  • Combinar 'only-cache'+'only-no-store' ou 'force-cache'+'force-no-store' na mesma rota: não permitido, gera erro.
  • Parent com 'default-no-store' e child com 'auto'/'*-cache': comportamento inconsistente do mesmo fetch.
  • revalidate = 60 * 10: não é estaticamente analisável; usar revalidate = 600.
  • Esperar revalidateTag/revalidatePath funcionar em dev: páginas em dev sempre renderizam sob demanda, nunca cacheadas.

Key Takeaways

  1. Este é o modelo pré-Cache Components: controle manual via opções de fetch, unstable_cache e route segment config, não use cache directive.
  2. dynamic e fetchCache operam em nível de rota inteira; revalidate de fetch individual pode ser mais agressivo que o default da rota, nunca menos.
  3. Deduplicação de fetch é automática; para ORM/DB direto, usar React cache().
  4. revalidateTag/revalidatePath invalidam o cache do Next.js, não CDN, então CDN precisa de purge separado.

Connects To

  • Caching (getting-started, Cache Components): modelo atual que substitui este para projetos com cacheComponents: true.
  • CDN Caching: revalidação on-demand não propaga pra CDN sozinha, precisa purge explícito.
  • Incremental Static Regeneration: usa generateStaticParams + revalidate deste modelo pra prerenderizar rotas dinâmicas.