Fundamentos do Next.js 15

0/26 aulas0%
teoria

Estratégias de Cache no Next.js 15: Data Cache e Revalidação

Aprenda sobre estratégias de cache no next.js 15: data cache e revalidação

35 min
Aula 4 de 5

Estratégias de Cache no Next.js 15: Data Cache e Revalidação

Bem-vindos ao módulo de Gerenciamento de Dados e Interatividade! Nesta aula, vamos desvendar um dos pilares da performance em aplicações Next.js: as estratégias de cache, com foco no Data Cache e na Revalidação.


🚀 1. Introdução: A Importância do Cache no Next.js 15

No mundo do desenvolvimento web, a velocidade é crucial. Usuários esperam que as páginas carreguem rapidamente e que os dados estejam sempre atualizados. O cache é uma técnica fundamental para atingir esses objetivos, armazenando temporariamente dados ou resultados de operações caras para que possam ser reutilizados sem a necessidade de reprocessamento.

Next.js 15, construído sobre o React Server Components, introduz um sistema de cache robusto e integrado que otimiza a forma como seus dados são buscados e servidos. O coração desse sistema é o Data Cache, que armazena os resultados de requisições fetch() e outras funções de busca de dados, especialmente em Server Components.

Nesta aula, exploraremos:

  • O que é o Data Cache e como ele funciona automaticamente.
  • Como controlar o cache de fetch() com opções específicas.
  • As poderosas estratégias de revalidação (baseada em tempo e sob demanda) para manter seus dados frescos.
  • Outras utilidades de cache como cache() e unstable_noStore().

Vamos mergulhar! 🏊‍♂️


🧠 2. Explicação Detalhada: Data Cache e Revalidação

Next.js 15 estende a API nativa fetch() do JavaScript para incluir recursos de cache e revalidação diretamente. Isso significa que, por padrão, as requisições fetch() feitas em Server Components são automaticamente cacheadas.

2.1. O Data Cache: Seu Aliado na Performance

O Data Cache é um mecanismo de cache persistente que armazena os resultados de requisições fetch() (e funções cacheadas com cache() ou React.cache()) no servidor. Quando uma requisição idêntica é feita novamente, Next.js pode servir os dados cacheados em vez de fazer uma nova requisição à fonte de dados original, acelerando significativamente o tempo de carregamento.

Como funciona com fetch():

Por padrão, toda requisição fetch() feita em um Server Component ou Route Handler é automaticamente cacheadas se a requisição for GET e não tiver headers que desabilitem o cache (como Cache-Control: no-store).

// app/page.tsx
async function getPosts() {
  // Esta requisição será automaticamente cacheada pelo Next.js
  // O resultado será armazenado no Data Cache
  const res = await fetch('https://api.example.com/posts');
  if (!res.ok) {
    throw new Error('Falha ao buscar posts');
  }
  return res.json();
}
 
export default async function HomePage() {
  const posts = await getPosts();
  return (
    <div>
      <h1>Meus Posts</h1>
      <ul>
        {posts.map((post: any) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

No exemplo acima, fetch('https://api.example.com/posts') será cacheada. Se a página for renderizada novamente (por exemplo, após uma navegação suave ou um refresh), e os dados ainda estiverem no cache, Next.js servirá a versão cacheada.

2.2. Opções de Cache com fetch()

A API fetch() no Next.js 15 permite um controle granular sobre o comportamento do cache através da opção cache:

  • 'force-cache' (Padrão): Cacheia a requisição e reutiliza os dados cacheados. Se os dados não estiverem no cache, eles serão buscados e, em seguida, cacheados.
    fetch('https://api.example.com/data', { cache: 'force-cache' });
  • 'no-store': Desabilita totalmente o cache para esta requisição. Os dados serão sempre buscados no momento da requisição.
    fetch('https://api.example.com/highly-dynamic-data', { cache: 'no-store' });
    👉 Quando usar? Para dados que mudam constantemente e precisam estar sempre atualizados (ex: cotações de ações em tempo real, dados de sessão de usuário).
  • 'no-cache': Reutiliza dados cacheados se disponíveis, mas sempre revalida (verifica a frescura) com o servidor de origem antes de usar. Se o servidor indicar que a versão cacheada ainda é válida, ela é usada. Caso contrário, uma nova requisição é feita.
    fetch('https://api.example.com/some-data', { cache: 'no-cache' });
    👉 Atenção: Embora exista, no-cache é menos comum de ser usado diretamente no Next.js para controle do Data Cache, pois a revalidação baseada em tempo (next.revalidate) ou sob demanda (revalidateTag) oferece um controle mais poderoso e explícito.

2.3. Revalidação: Mantendo Seus Dados Frescos

O cache é ótimo para performance, mas os dados podem ficar desatualizados. A revalidação é o processo de limpar o cache e buscar novos dados. Next.js oferece duas estratégias principais:

2.3.1. Revalidação Baseada em Tempo (Time-based Revalidation)

Similar ao conceito de ISR (Incremental Static Regeneration) para páginas, você pode configurar fetch() para revalidar dados após um certo período de tempo.

  • next.revalidate: Adicione a propriedade next: { revalidate: <seconds> } ao segundo argumento de fetch().
// app/products/page.tsx
async function getProducts() {
  const res = await fetch('https://api.example.com/products', {
    // Revalida os dados a cada 60 segundos
    next: { revalidate: 60 }
  });
  if (!res.ok) {
    throw new Error('Falha ao buscar produtos');
  }
  return res.json();
}
 
export default async function ProductsPage() {
  const products = await getProducts();
  return (
    <div>
      <h1>Nossos Produtos</h1>
      <ul>
        {products.map((product: any) => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  );
}

Neste exemplo, a primeira requisição buscará e cacheará os produtos. Requisições subsequentes dentro de 60 segundos usarão o cache. Após 60 segundos, a próxima requisição ainda servirá o dado cacheado enquanto uma nova requisição em background é feita para atualizar o cache. Isso garante que os usuários sempre vejam conteúdo imediatamente, mesmo que ligeiramente desatualizado, enquanto o novo conteúdo é buscado.

2.3.2. Revalidação Sob Demanda (On-demand Revalidation)

Para cenários onde os dados precisam ser atualizados imediatamente após uma mudança (ex: um post de blog é publicado, um item é adicionado ao carrinho), a revalidação sob demanda é a solução. Você pode acionar a revalidação programaticamente usando revalidatePath() ou revalidateTag().

Para usar a revalidação sob demanda, você precisa "marcar" suas requisições fetch() com tags.

  • next.tags: Adicione a propriedade next: { tags: ['tag1', 'tag2'] } ao segundo argumento de fetch().
// app/blog/[slug]/page.tsx
async function getBlogPost(slug: string) {
  const res = await fetch(`https://api.example.com/blog/${slug}`, {
    // Marca esta requisição com a tag 'blog-posts'
    // e também uma tag específica para este post
    next: { tags: ['blog-posts', `post-${slug}`] }
  });
  if (!res.ok) {
    throw new Error('Falha ao buscar post');
  }
  return res.json();
}
 
export default async function BlogPostPage({ params }: { params: { slug: string } }) {
  const post = await getBlogPost(params.slug);
  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </div>
  );
}

Agora, para revalidar esses dados, você pode usar:

  • revalidatePath(path: string): Limpa o cache para todos os dados associados a um determinado path (caminho da URL).

    // app/api/revalidate-post/route.ts (Exemplo de Route Handler)
    import { revalidatePath } from 'next/cache';
    import { NextRequest, NextResponse } from 'next/server';
     
    export async function GET(request: NextRequest) {
      const path = request.nextUrl.searchParams.get('path');
     
      if (path) {
        revalidatePath(path);
        return NextResponse.json({ revalidated: true, now: Date.now() });
      }
     
      return NextResponse.json({ revalidated: false, message: 'Missing path to revalidate' });
    }

    👉 Uso: Chame revalidatePath('/blog/meu-post-incrivel') para limpar o cache da página específica.

  • revalidateTag(tag: string): Limpa o cache para todas as requisições fetch() que foram marcadas com a tag especificada.

    // app/api/revalidate-blog/route.ts (Exemplo de Route Handler)
    import { revalidateTag } from 'next/cache';
    import { NextRequest, NextResponse } from 'next/server';
     
    export async function GET(request: NextRequest) {
      const tag = request.nextUrl.searchParams.get('tag');
     
      if (tag) {
        revalidateTag(tag);
        return NextResponse.json({ revalidated: true, now: Date.now() });
      }
     
      return NextResponse.json({ revalidated: false, message: 'Missing tag to revalidate' });
    }

    👉 Uso: Chame revalidateTag('blog-posts') para limpar o cache de todos os posts de blog, ou revalidateTag('post-meu-post-incrivel') para um post específico.

Ambas as funções revalidatePath e revalidateTag são tipicamente chamadas de Server Actions ou Route Handlers, pois precisam ser executadas no servidor.

2.4. cache() Utility de next/cache

Para funções que não usam fetch() (ex: acessando um banco de dados diretamente, usando uma biblioteca como axios ou graphql-request), você pode usar a função cache() de next/cache para memoizar seus resultados.

// lib/db.ts
import { cache } from 'next/cache';
 
// Exemplo de função que busca dados diretamente de um DB ou ORM
async function getUser(id: string) {
  // Simula uma operação de banco de dados
  console.log(`Buscando usuário ${id} do DB...`);
  await new Promise(resolve => setTimeout(resolve, 1000)); // Simula latência
  return { id, name: `Usuário ${id}`, email: `user${id}@example.com` };
}
 
// Cacheia a função getUser
export const getCachedUser = cache(getUser);
 
// app/profile/[id]/page.tsx
import { getCachedUser } from '@/lib/db';
 
export default async function ProfilePage({ params }: { params: { id: string } }) {
  const user = await getCachedUser(params.id); // Esta chamada será cacheada
  const userAgain = await getCachedUser(params.id); // Retorna do cache sem executar getUser novamente
 
  return (
    <div>
      <h1>Perfil de {user.name}</h1>
      <p>Email: {user.email}</p>
    </div>
  );
}

Observações:

  • cache() memoiza os resultados da função no servidor.
  • Não possui opções de revalidação baseada em tempo (revalidate) ou tags. Para revalidar dados cacheados por cache(), você precisaria revalidar o path que a invoca, ou reiniciar o servidor de desenvolvimento.

2.5. unstable_noStore(): Desabilitando o Cache Completamente

Em alguns casos, você pode precisar garantir que um componente ou um segmento de dados nunca seja cacheado, mesmo que use fetch() ou outras funções que seriam cacheadas por padrão. Para isso, use unstable_noStore() de next/cache.

// app/dashboard/page.tsx
import { unstable_noStore } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';
 
async function getRealtimeMetrics() {
  unstable_noStore(); // Garante que esta função e o componente que a chama não sejam cacheados
  const res = await fetch('https://api.example.com/realtime-metrics');
  if (!res.ok) {
    throw new Error('Falha ao buscar métricas');
  }
  return res.json();
}
 
export default async function DashboardPage() {
  const metrics = await getRealtimeMetrics();
  return (
    <div>
      <h1>Dashboard em Tempo Real</h1>
      <p>Usuários Ativos: {metrics.activeUsers}</p>
      <p>Novas Vendas: {metrics.newSales}</p>
    </div>
  );
}

👉 Quando usar? Para dados extremamente dinâmicos que precisam ser sempre os mais recentes, ou para componentes que exibem informações sensíveis ao tempo que não podem ser servidas do cache de forma alguma.

2.6. Interação com Client Components

É importante lembrar que o Data Cache do Next.js 15 opera principalmente no servidor.

  • fetch() em Client Components: Se você usar fetch() diretamente em um Client Component, ele se comportará como um fetch() padrão do navegador. Ele não será automaticamente cacheado pelo Data Cache do Next.js, nem terá as opções next.revalidate ou next.tags. Para cache e revalidação em Client Components, você geralmente usaria bibliotecas como SWR ou React Query, ou buscaria dados através de Route Handlers/Server Actions que, por sua vez, podem usar o Data Cache.

💡 3. Exercícios/Desafios Conceituais

Para consolidar seu entendimento, pense nos seguintes cenários:

  1. Cenário 1: Blog de Notícias Você está desenvolvendo um site de notícias. Os artigos são atualizados algumas vezes ao dia, mas não a cada minuto. Os comentários de cada artigo, no entanto, são adicionados com frequência e precisam ser exibidos quase em tempo real.

    • Que estratégia de cache você usaria para buscar os artigos? Por quê?
    • Que estratégia você usaria para buscar os comentários? Por quê?
  2. Cenário 2: Catálogo de Produtos Um e-commerce tem um catálogo de produtos. Os dados dos produtos raramente mudam, mas os preços podem ser atualizados por um administrador. Quando um preço é atualizado no painel de administração, ele precisa ser refletido no site imediatamente.

    • Como você configuraria o fetch() para os dados dos produtos?
    • Como você implementaria a atualização imediata dos preços após a ação do administrador?
  3. Cenário 3: Painel de Controle de Usuário Um painel de controle exibe informações sensíveis do usuário (ex: saldo da conta, histórico de transações). Essas informações são altamente personalizadas e devem ser sempre as mais recentes.

    • Que opção de cache você usaria para buscar esses dados? Há alguma função auxiliar que você consideraria usar?

📝 4. Resumo e Próximos Passos

Nesta aula, exploramos as poderosas estratégias de cache e revalidação no Next.js 15:

  • Data Cache: O cache automático de fetch() em Server Components.
  • Opções de fetch(): 'force-cache' (padrão), 'no-store', 'no-cache' para controle granular.
  • Revalidação Baseada em Tempo: Usando next: { revalidate: <seconds> } para atualizar dados periodicamente.
  • Revalidação Sob Demanda: Usando next: { tags: [...] } com revalidatePath() e revalidateTag() para atualizações imediatas.
  • cache(): Para memoizar funções que não usam fetch().
  • unstable_noStore(): Para desabilitar completamente o cache para dados altamente dinâmicos.

Dominar essas técnicas é fundamental para construir aplicações Next.js rápidas, eficientes e com dados sempre atualizados.

No próximo tópico, aprofundaremos como essas estratégias se integram com Server Actions e Mutations, permitindo que você modifique dados e revalide o cache de forma transparente e performática! 🔜

© 2025 Escola All Dev. Todos os direitos reservados.

Estratégias de Cache no Next.js 15: Data Cache e Revalidação - Fundamentos do Next.js 15 | escola.all.dev.br