Next.js: aprimorando performance e escalabilidade com sua API de cache

Next.js: aprimorando performance e escalabilidade com sua API de cache
Patrícia Silva
Patrícia Silva

Compartilhe

Sabe quando você vai na sua pizzaria favorita e o garçom já sabe o que você vai pedir?

É como se ele chegasse perguntando: "Vai no de sempre, chefia?" e então ele já pega a pizza que está pronta no balcão.

E, pronto! Você não precisa esperar pelo preparo da pizza na cozinha.

É sobre isso que vamos abordar aqui neste artigo. Não sobre pizza, claro. Mas sobre API de cache.

Então, pegue sua fatia e bora lá! :)

API de cache no Next.JS

Recentemente, na versão 14, a equipe do Next.js introduziu uma nova API de cache.

O objetivo principal é simplificar o armazenamento de operações custosas, como consultas a bancos de dados, e permitir a reutilização desse cache em várias requisições.

É mais ou menos assim que funciona o cache: uma vez que já foi feito o pedido, os dados são armazenados.

Isso significa que não é necessário acessar novamente o banco de dados para recuperar esses dados, pois já estão disponíveis na camada de cache.

Esta camada fica mais próxima do usuário, geralmente no próprio navegador, facilitando e acelerando o acesso às informações.

O fluxo de cache de maneira simplificada funciona assim: ao fazer uma chamada de API, primeiro o Next verifica o cache, se a resposta para essa chamada já está lá, pegamos a resposta do cache e a enviamos para o cliente.

Se não estiver, chamamos o servidor para obter a resposta HTTP. A resposta do servidor é salva primeiro no cache antes de ser enviada ao cliente.

Diagrama de requisição HTTP cliente servidor com a representação do cache como uma camada intermediária. Banner promocional da Alura, com um design futurista em tons de azul, apresentando o texto

Vantagens do Cache API

Ao armazenar dados frequentemente acessados no cache, conseguimos reduzir o tempo de resposta, pois as informações podem ser recuperadas rapidamente sem a necessidade de consultas adicionais ao servidor ou banco de dados.

Isso não só melhora o carregamento das páginas, como também economiza recursos e aumenta a escalabilidade da aplicação.

Além disso, ao reduzir a latência e melhorar a velocidade, o cache contribui para uma navegação mais fluída e satisfatória para o usuário, melhorando a experiência geral do usuário.

É importante salientar que a Cache API do Next.js não é equivalente à Cache API do React.

Embora ambas forneçam funcionalidades similares, os seus casos de uso diferem. A Cache API do Next.js armazena requisições fetch, enquanto a Cache API do React guarda o componente inteiro, sendo destinada apenas ao uso com React Server Components.

Data Fetching com a Cache API

A cache API pode ser utilizada para otimizar o data fetching em aplicações Next.js.

Ao usar a função fetch, você pode configurar o comportamento de caching e revalidação para cada requisição ao servidor.

E o que é a revalidação, porque fazer a revalidação é importante?

Quando se trabalha com cache, uma das coisas que mais vamos ouvir é o termo, revalidação de cache, do qual refere-se ao processo de atualização dos dados armazenados no cache.

Por exemplo: no data fetching, quando você usa a função fetch, tem a opção de configurar não apenas como e quando os dados são armazenados em cache, mas também como eles são revalidados.

Isso significa que você pode definir regras para verificar se os dados no cache ainda estão atualizados em comparação com os dados no servidor.

Se os dados no cache forem considerados desatualizados ou inválidos, o sistema automaticamente faz uma nova requisição ao servidor para buscar e armazenar a versão mais recente dos dados, garantindo assim que o usuário sempre tenha acesso às informações mais atualizadas.

A revalidação é uma parte importante para manter o equilíbrio entre desempenho (usando dados em cache para respostas mais rápidas) e precisão (garantindo que os dados não estejam desatualizados), contribuindo para a eficiência e a experiência do usuário em aplicações web.

Como é que se utiliza a API de cache Next.js?

Bem, a sintaxe da API da cache tem o seguinte anatomia:

const data = unstable_cache(fetchData, keyParts, options)()

A função `fectData' (obrigatória) é uma função assíncrona que você usa para obter os dados. Essa função é especial porque trabalha de forma assíncrona, ou seja, ela pode realizar suas tarefas sem bloquear outras partes do seu aplicativo.

O keyParts (opcional) serve para identificar dados no cache. Ou seja, para armazenar esses dados de forma eficiente, você utiliza uma chave de cache.

Esta chave é única e ajuda a identificar exatamente quais dados estão sendo armazenados.

Já o options é um objeto que controla o comportamento do cache com as seguintes propriedades:

  • tags: você pode usar tags para organizar e gerenciar seus dados em cache. As tags são como etiquetas que ajudam a identificar e agrupar os dados.
  • revalidate: aqui você define quanto tempo os dados devem permanecer no cache antes de serem atualizados. Isso é medido em segundos.

Aqui está um exemplo:

'use server'

import { unstable_cache } from "next/cache";
import prisma from '../lib/prisma';

async function Page({ user }) {
    // Define a função que busca os posts do usuário
    const fetchPosts = async (user) => {
        // Consulta ao banco de dados usando Prisma para obter os posts
        const posts = await prisma.post.findMany({
            where: { user: user },
            take: 10
        });
        return posts;
    };

    // Uso do cache do Next.js com a função de busca de posts
    const posts = await unstable_cache(
        () => fetchPosts(user),
        undefined,
        { tags: [`posts_${user}`], revalidate: 60 }
    );

    // ... resto do componente
}

export default Page;

Aqui, está sendo utilizado o unstable_cache do Next.js para armazenar em cache o resultado da consulta ao banco de dados. Isso significa que a função fetchPosts é executada e os seus resultados são armazenados em cache.

A próxima vez que a mesma consulta for feita (com os mesmos parâmetros), o Next.js pode entregar os dados diretamente do cache em vez de executar a consulta novamente.

Isso é especialmente útil para melhorar o desempenho e reduzir a carga no banco de dados.

Olha que bacana o uso da opção tags em ação, no exemplo, a tag está sendo construída usando uma string posts_ seguida do identificador do usuário (user).

Por exemplo, se o usuário tiver um identificador 123, a tag seria posts_123.

A grande vantagem aqui, é que se quisermos invalidar ou atualizar todos os dados em cache relacionados a posts de um usuário específico, pode fazer isso facilmente referenciando esta tag.

Por último mas não menos importante, foi aplicado o revalidate, onde ele é o responsável pelo período de tempo (em segundos) após o qual os dados em cache são considerados desatualizados e, portanto, precisam ser revalidados ou atualizados.

No caso de revalidate: 60, isso significa que 60 segundos após os dados serem armazenados no cache, eles serão marcados para revalidação.

A grande vantagem aqui, é que isso garante que os dados não fiquem desatualizados por muito tempo.

Se os dados são críticos e mudam frequentemente, um tempo menor de revalidação pode ser ideal. Em contrapartida, se os dados mudam raramente, um tempo maior pode ser mais apropriado.

Para consultar mais detalhes sobre o unstable_cache visite a documentação oficial.

Vamos analisar outro exemplo?

Bem, aqui está sendo utilizado o unstable_cache para revalidar o cache após a inserção ou atualização do post, o que quero dizer é que isso assegura que os dados mais recentes sejam mostrados ao usuário.

import { unstable_cache } from "next/cache";
import prisma from '../lib/prisma';

export function NewPostForm() {
    'use server';

    async function savePost(event) {
        event.preventDefault();

        // Obter os valores do formulário
        const formData = new FormData(event.target);
        const post = {
            user: formData.get('user'),
            content: formData.get('content'),
        };

        // Insere ou atualiza o post no banco de dados usando Prisma
        const savedPost = await prisma.post.upsert({
            where: { user: post.user },
            update: { content: post.content },
            create: { ...post }
        });

        // Revalida o cache do usuário
        await unstable_cache.revalidate(`posts_${post.user}`);

        // Redireciona ou atualiza a página, ou lida com o post de outra forma
        // ...
    }

    return (
        <form onSubmit={savePost}>
            {/* Campos do formulário aqui */}
            <input type="text" name="user" required />
            <textarea name="content" required></textarea>
            <button type="submit">Salvar Post</button>
        </form>
    );
}

Perguntas que podem aparecer na sua cabeça agora: “por que usar o unstable_cache do Next.js se existem alternativas como SWR e React-query”?

Bem, vamos colocar os pingos nos “i”s.

No Next.js versão 12, muitos de nós optávamos por soluções externas, como a biblioteca SWR, ou recorríamos a funções nativas como getStaticProps para lidar com o gerenciamento de cache e a busca de dados, principalmente do lado do servidor.

Essas estratégias se mostraram eficientes para armazenar dados estáticos, acelerando o carregamento das páginas.

Com o avanço para a versão 14 do Next.js, houve uma mudança significativa. Agora, o Next.js dá mais ênfase ao uso do método fetchque é nativo por natureza, para lidar com caching, operando tanto no lado do cliente (navegador) quanto no servidor (node).

Esta abordagem parece ser uma tentativa de padronizar a maneira como os dados são obtidos, independente de onde a aplicação está sendo executada.

Entretanto, surge um novo desafio: na versão 14, para aqueles que desejam desenvolver funções personalizadas de busca de dados com ferramentas modernas como o Prisma, a solução disponível é o unstable_cache.

Mas, vale ressaltar que esta ainda é uma funcionalidade em desenvolvimento, pois é experimental.

Isso significa que o unstable_cache pode passar por mudanças e talvez não seja a opção mais confiável para ambientes de produção neste momento.

Para maiores detalhes sobre caching em geral com Next.js visite a documentação.

Conclusão

O mais interessante é que enquanto o Next.js continua evoluindo, especialmente no que tange ao gerenciamento eficiente de cache e recuperação de dados, ainda estamos em um estágio de transição e aprendizado.

Mas caminhando para uma solução mais nativa, em que o suporte de cache nativo por parte do Next, irá facilitar muito o gerenciamento flexível e granular que ele se propõe a resolver com a nova cache API.

Se quiser se aprofundar no assunto

Aqui na Alura, temos vários conteúdos sobre Next, vamos conhecê-las?

Além disso, você vai aprender conceitos-chave da ferramenta e ver na prática como criar suas primeiras páginas usando o framework.

Nessa formação, trabalhamos com a versão 13 do Next. Nela, você aprenderá o que é e como construir um Design System usando as tecnologias mais populares de desenvolvimento web.

Aproveite e veja os conteúdos que a comunidade Next divulgou na última conferência.

Compartilhe o que você aprendeu: E vamos lançar um desafio! Se você gostou desse artigo, compartilhe nas redes sociais o que você aprendeu com ele com a hashtag #AprendinoBlogdaAlura.

Patrícia Silva
Patrícia Silva

Sou Engenheira de Software, atualmente atuando como Fullstack Engineer e baseada em Portugal. Sou uma profissional entusiasmada que ama tecnologia. Trabalho como desenvolvedora web há mais de 15 anos. Ajudo desenvolvedores a melhorar suas habilidades e estou aberta a trocas de conhecimento com qualquer pessoa. Sou mãe de plantas e de dois meninos lindos. Adoro viajar, conhecer novas pessoas e estar em contato com a natureza. O foco e o cuidado com a experiência do usuário são o segredo do sucesso.

Veja outros artigos sobre Front-end