O Cache do App Router do Next.js, Sem Mistério
O cache no App Router do Next.js tem fama de confuso, e boa parte dessa fama é merecida — o modelo mental mudou entre versões, e o conselho de uma versão nem sempre se aplica bem à seguinte. Em vez de tentar enumerar todas as camadas de cache de forma abstrata, é mais útil partir de um exemplo concreto e funcional e construir o modelo mental a partir dele.
O exemplo concreto: cacheando uma chamada à API do GitHub
Este site mostra contagens de estrelas e forks em tempo real para os projetos em destaque, puxadas da API REST do GitHub. Chamar essa API a cada visualização de página seria desperdício e arriscaria bater no rate limit do GitHub, então a chamada é cacheada e só é atualizada periodicamente:
async function getRepoStats(repo: string): Promise<RepoStats | null> {
const response = await fetch(
`https://api.github.com/repos/${GITHUB_OWNER}/${repo}`,
{ next: { revalidate: 3600 } }
);
if (!response.ok) {
console.warn(`GitHub API returned ${response.status} for ${repo}`);
return null;
}
const data = await response.json();
return { stars: data.stargazers_count, forks: data.forks_count, updatedAt: data.updated_at };
}
A linha interessante é next: { revalidate: 3600 }. Ela diz ao Next.js: guarde em cache o resultado desta chamada fetch específica, e trate como atualizado por até uma hora. Depois que essa hora passa, a próxima requisição que precisar desses dados dispara uma atualização em segundo plano — a página que dispara essa atualização ainda recebe o dado em cache (agora levemente desatualizado) imediatamente, e requisições seguintes recebem a versão recém-atualizada. Isso é Incremental Static Regeneration aplicado no nível de uma única chamada fetch, em vez de uma página inteira.
Por que a degradação graciosa importa tanto quanto o cache
Repare que o branch if (!response.ok) retorna null em vez de lançar um erro. Isso não é sobre cache diretamente, mas é inseparável de uma boa estratégia de cache: se o GitHub estiver fora do ar ou com rate limit atingido, você não quer que uma falha passageira do serviço externo derrube sua página. Quem chama getRepoStats cai de volta para dados curados e escritos à mão — o site continua totalmente funcional, só temporariamente sem as contagens de estrelas ao vivo. O cache reduz a frequência com que você pergunta dados a um serviço externo não confiável; a degradação graciosa trata o que acontece nas ocasiões (mais raras, mas reais) em que essa pergunta falha mesmo assim. Um não substitui o outro.
O que de fato muda entre versões do Next.js
Aqui vai a ressalva honesta: o modelo geral de cache do App Router — quantas camadas distintas existem, o que é cacheado por padrão versus opt-in, como a renderização de Server Components interage com o cache em nível de fetch — evoluiu de forma significativa entre versões do Next.js, e continua evoluindo. Se você está lendo isso algum tempo depois de ter sido escrito, ou trabalhando numa versão bem mais nova ou mais antiga do que um tutorial assume, os padrões específicos podem não bater com o que você vê. O padrão fetch + next.revalidate mostrado acima tem sido um mecanismo estável e bem documentado entre versões, e é exatamente por isso que vale a pena ancorar seu modelo mental nele, em vez de numa foto instantânea de "tudo que é cacheado e por quê" — essa foto muda; uma chamada fetch com uma janela de revalidação explícita, não.
O aprendizado prático
Quando você não tiver certeza do que está em cache e por quanto tempo num projeto App Router específico, a forma mais rápida de ganhar confiança não é decorar as regras de cache atuais — é encontrar cada chamada fetch e verificar se ela tem um revalidate explícito (ou cache: "no-store", ou nada, o que recai no padrão do framework naquele momento). Essa única pergunta — "essa requisição específica está em cache, e por quanto tempo" — responde o que realmente importa na prática, e é a única coisa que continua legível mesmo quando os padrões do framework mudam por baixo dela.
Gerado por Claude Sonnet 5 (seeded manually via Claude Code) · 29 de junho de 2026 · verificado em build antes da publicação