O autor selecionou /dev/color para receber uma doação como parte do programa Escreva por Doações.
Introdução
A maioria das aplicações depende de dados, seja de um banco de dados ou de uma API. Buscar dados de uma API envia uma solicitação de rede para o servidor da API e retorna os dados como resposta. Essas idas e vindas levam tempo e podem aumentar o tempo de resposta da sua aplicação para os usuários. Além disso, a maioria das APIs limita o número de solicitações que podem servir a uma aplicação dentro de um período de tempo específico, um processo conhecido como limitação de taxa.
Para contornar esses problemas, você pode armazenar em cache seus dados para que a aplicação faça uma única solicitação a uma API, e todas as solicitações de dados subsequentes recuperarão os dados do cache. O Redis, um banco de dados em memória que armazena dados na memória do servidor, é uma ferramenta popular para armazenar em cache dados. Você pode se conectar ao Redis no Node.js usando o node-redis
módulo, que oferece métodos para recuperar e armazenar dados no Redis.
Neste tutorial, você vai construir uma aplicação Express que obtém dados de uma API RESTful usando o módulo axios
. Em seguida, você vai modificar a aplicação para armazenar os dados obtidos da API no Redis usando o módulo node-redis
. Depois disso, você vai implementar o período de validade do cache para que o cache expire após um certo período de tempo. Por fim, você vai usar o middleware Express para armazenar em cache os dados.
Pré-requisitos
Para seguir o tutorial, você vai precisar de:
-
Configuração do ambiente Node.js no seu servidor. Se estiver no Ubuntu 22.04, instale a versão mais recente do Node.js e npm seguindo a opção 3 em Como Instalar o Node.js no Ubuntu 22.04. Para outros sistemas operacionais, consulte a série Como Instalar o Node.js e Criar um Ambiente de Desenvolvimento Local.
-
O Redis está instalado no seu servidor. Se estiver utilizando o Ubuntu 22.04, siga os passos 1 e 2 de Como Instalar e Proteger o Redis no Ubuntu 22.04. Se estiver trabalhando em outro sistema operacional, consulte Como Instalar e Proteger o Redis.
-
Conhecimento de programação assíncrona. Siga Compreensão do Event Loop, Callbacks, Promises e Async/Await em JavaScript.
-
Conhecimento básico usando o framework web Express. Veja Como Começar com Node.js e Express.
Passo 1 — Configurando o Projeto
Neste passo, você instalará as dependências necessárias para este projeto e iniciará um servidor Express. Neste tutorial, você criará uma wiki contendo informações sobre diferentes tipos de peixes. Chamaremos o projeto de fish_wiki
.
Primeiro, crie o diretório para o projeto usando o comando mkdir
:
Mova-se para o diretório:
Inicialize o arquivo package.json
usando o comando npm
:
A opção -y
aceita automaticamente todos os padrões.
Ao executar o comando npm init
, ele criará o arquivo package.json
no seu diretório com o seguinte conteúdo:
OutputWrote to /home/your_username/<^>fish_wiki<^/package.json:
{
"name": "fish_wiki",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Em seguida, você irá instalar os seguintes pacotes:
express
: um framework de servidor web para Node.js.axios
: um cliente HTTP Node.js, útil para fazer chamadas de API.node-redis
: um cliente Redis que permite armazenar e acessar dados no Redis.
Para instalar os três pacotes juntos, insira o seguinte comando:
Após instalar os pacotes, você criará um servidor Express básico.
Usando o nano
ou o editor de texto de sua escolha, crie e abra o arquivo server.js
:
No seu arquivo server.js
, insira o seguinte código para criar um servidor Express:
Primeiro, importe o express
para o arquivo. Na segunda linha, defina a variável app
como uma instância de express
, que lhe dá acesso a métodos como get
, post
, listen
e muitos outros. Este tutorial se concentrará nos métodos get
e listen
.
Na linha seguinte, defina e atribua a variável port
ao número da porta na qual deseja que o servidor escute. Se nenhum número de porta estiver disponível em um arquivo de variáveis ambientais, a porta 3000
será usada como padrão.
Finalmente, utilizando a variável app
, você invoca o método listen()
do módulo express
para iniciar o servidor na porta 3000
.
Salve e feche o arquivo.
Execute o arquivo server.js
usando o comando node
para iniciar o servidor:
O console irá registrar uma mensagem semelhante à seguinte:
OutputApp listening on port 3000
A saída confirma que o servidor está em execução e pronto para atender a quaisquer solicitações na porta 3000
. Como o Node.js não recarrega automaticamente o servidor quando os arquivos são alterados, agora você irá parar o servidor usando CTRL+C
para que possa atualizar server.js
no próximo passo.
Depois de ter instalado as dependências e criado um servidor Express, você irá recuperar dados de uma API RESTful.
Passo 2 — Recuperando Dados de uma API RESTful Sem Caching
Neste passo, você irá construir sobre o servidor Express do passo anterior para recuperar dados de uma API RESTful sem implementar cache, demonstrando o que acontece quando os dados não são armazenados em um cache.
Para começar, abra o arquivo server.js
no seu editor de texto:
Em seguida, você irá recuperar dados da API FishWatch. A API FishWatch retorna informações sobre espécies de peixes.
No seu arquivo server.js
, defina uma função que solicita dados da API com o seguinte código destacado:
Na segunda linha, você importa o módulo axios
. Em seguida, você define uma função assíncrona fetchApiData()
, que recebe species
como parâmetro. Para tornar a função assíncrona, você a prefixa com a palavra-chave async
.
Dentro da função, você chama o método get()
do módulo axios
com o endpoint da API de onde deseja que o método recupere os dados, que é a API FishWatch neste exemplo. Como o método get()
implementa uma promessa, você a prefixa com a palavra-chave await
para resolver a promessa. Assim que a promessa for resolvida e os dados forem retornados pela API, você chama o método console.log()
. O método console.log()
irá registrar uma mensagem informando que uma solicitação foi enviada para a API. Por fim, você retorna os dados da API.
Em seguida, você definirá uma rota Express que aceita solicitações GET
. No seu arquivo server.js
, defina a rota com o seguinte código:
No bloco de código anterior, você invoca o método get()
do módulo express
, que apenas escuta as solicitações GET
. O método recebe dois argumentos:
/fish/:espécie
: o endpoint que o Express estará ouvindo. O endpoint recebe um parâmetro de rota:espécie
que captura qualquer coisa inserida nessa posição na URL.getSpeciesData()
(ainda não definido): uma função de retorno de chamada que será chamada quando a URL corresponder ao endpoint especificado no primeiro argumento.
Agora que a rota está definida, especifique a função de retorno de chamada getSpeciesData
:
A função getSpeciesData
é uma função de manipulador assíncrona passada para o método get()
do módulo express
como segundo argumento. A função getSpeciesData()
recebe dois argumentos: um objeto de requisição e um objeto de resposta. O objeto de requisição contém informações sobre o cliente, enquanto o objeto de resposta contém as informações que serão enviadas ao cliente pelo Express.
Em seguida, adicione o código destacado para chamar fetchApiData()
para recuperar dados de uma API na função de retorno de chamada getSpeciesData()
:
Na função, você extrai o valor capturado do endpoint armazenado no objeto req.params
, em seguida, atribui-o à variável espécie
. Na próxima linha, você define a variável resultados
e a define como indefinida
.
Depois disso, você invoca a função fetchApiData()
com a variável species
como argumento. A chamada da função fetchApiData()
é prefixada com a sintaxe await
porque ela retorna uma promessa. Quando a promessa é resolvida, ela retorna os dados, que são então atribuídos à variável results
.
Em seguida, adicione o código destacado para lidar com erros de tempo de execução:
Você define o bloco try/catch
para lidar com erros de tempo de execução. No bloco try
, você chama fetchApiData()
para recuperar dados de uma API. Se um erro for encontrado, o bloco catch
registra o erro e retorna um código de status 404
com uma resposta “Dados indisponíveis”.
A maioria das APIs retorna um código de status 404 quando não possuem dados para uma consulta específica, o que aciona automaticamente o bloco catch
para ser executado. No entanto, a API FishWatch retorna um código de status 200 com um array vazio quando não há dados para essa consulta específica. Um código de status 200 significa que a solicitação foi bem-sucedida, então o bloco catch()
nunca é acionado.
Para acionar o bloco catch()
, você precisa verificar se o array está vazio e lançar um erro quando a condição do if
avalia como verdadeira. Quando as condições do if
avaliam como falsas, você pode enviar uma resposta ao cliente contendo os dados.
Para fazer isso, adicione o código destacado:
Uma vez que os dados são retornados da API, a instrução if
verifica se a variável results
está vazia. Se a condição for atendida, você usa a instrução throw
para lançar um erro personalizado com a mensagem A API retornou uma matriz vazia
. Após sua execução, a execução passa para o bloco catch
, que registra a mensagem de erro e retorna uma resposta 404.
Por outro lado, se a variável results
tiver dados, a condição da instrução if
não será atendida. Como resultado, o programa irá ignorar o bloco if
e executar o método send
do objeto de resposta, que envia uma resposta para o cliente.
O método send
recebe um objeto que possui as seguintes propriedades:
-
fromCache
: a propriedade aceita um valor que ajuda a determinar se os dados estão vindo do cache Redis ou da API. Você atribuiu agora um valorfalse
porque os dados vêm de uma API. -
data
: a propriedade é atribuída à variávelresults
que contém os dados retornados da API.
Neste ponto, seu código completo terá a seguinte aparência:
Agora que tudo está no lugar, salve e saia do seu arquivo.
Inicie o servidor express:
A API Fishwatch aceita muitas espécies, mas vamos usar apenas a espécie de peixe red-snapper
como um parâmetro de rota no endpoint que você estará testando ao longo deste tutorial.
Agora, abra seu navegador da web favorito em seu computador local. Navegue até o URL http://localhost:3000/fish/red-snapper
.
Nota: Se você estiver seguindo o tutorial em um servidor remoto, você pode visualizar o aplicativo em seu navegador usando encaminhamento de porta.
Com o servidor Node.js ainda em execução, abra outro terminal em seu computador local e insira o seguinte comando:
Ao conectar-se ao servidor, navegue até http://localhost:3000/fish/red-snapper
em seu navegador da web no computador local.
Assim que a página carregar, você deverá ver fromCache
definido como false
.
Agora, atualize o URL mais três vezes e observe seu terminal. O terminal registrará “Request sent to the API” tantas vezes quantas você atualizou seu navegador.
Se você atualizou o URL três vezes após a visita inicial, sua saída será assim:
Output
App listening on port 3000
Request sent to the API
Request sent to the API
Request sent to the API
Request sent to the API
Esta saída mostra que uma solicitação de rede é enviada ao servidor da API toda vez que você atualiza o navegador. Se você tivesse um aplicativo com 1000 usuários acessando o mesmo endpoint, seriam 1000 solicitações de rede enviadas à API.
Ao implementar o cache, a solicitação à API será feita apenas uma vez. Todas as solicitações subsequentes obterão dados do cache, aumentando o desempenho do seu aplicativo.
Por enquanto, pare seu servidor Express com CTRL+C
.
Agora que você pode solicitar dados de uma API e servir aos usuários, você irá armazenar em cache os dados retornados de uma API no Redis.
Passo 3 — Armazenando em Cache Solicitações de API RESTful Usando o Redis
Nesta seção, você irá armazenar em cache os dados da API para que apenas a primeira visita ao endpoint do seu aplicativo solicite dados de um servidor de API, e todas as solicitações seguintes buscarão dados do cache do Redis.
Abra o arquivo server.js
:
No seu arquivo server.js
, importe o módulo node-redis
:
No mesmo arquivo, conecte-se ao Redis usando o módulo node-redis
adicionando o código destacado:
Primeiro, você define a variável redisClient
com o valor indefinido. Depois disso, você define uma função assíncrona anônima autoinvocada, que é uma função que é executada imediatamente após ser definida. Você define uma função assíncrona anônima autoinvocada, envolvendo uma definição de função sem nome entre parênteses (async () => {...})
. Para torná-la autoinvocada, você imediatamente a segue com outro conjunto de parênteses ()
, o que resulta em (async () => {...})()
.
Dentro da função, você invoca o método createClient()
do módulo redis
que cria um objeto redis
. Como você não forneceu a porta para o Redis usar quando invocou o método createClient()
, o Redis usará a porta 6379
, a porta padrão.
Você também chama o método on()
do Node.js que registra eventos no objeto Redis. O método on()
recebe dois argumentos: error
e um retorno de chamada. O primeiro argumento error
é um evento acionado quando o Redis encontra um erro. O segundo argumento é um retorno de chamada que é executado quando o evento error
é emitido. O retorno de chamada registra o erro no console.
Finalmente, você chama o método connect()
, que inicia a conexão com o Redis na porta padrão 6379
. O método connect()
retorna uma promessa, então você o prefixa com a sintaxe await
para resolvê-lo.
Agora que sua aplicação está conectada ao Redis, você modificará o retorno de chamada getSpeciesData()
para armazenar dados no Redis na visita inicial e recuperar os dados do cache para todas as solicitações que seguem.
No seu arquivo server.js
, adicione e atualize o código destacado:
Na função getSpeciesData
, você define a variável isCached
com o valor false
. Dentro do bloco try
, você chama o método get()
do módulo node-redis
com species
como argumento. Quando o método encontra a chave no Redis que corresponde ao valor da variável species
, ele retorna os dados, que são então atribuídos à variável cacheResults
.
Em seguida, uma declaração if
verifica se a variável cacheResults
possui dados. Se a condição for atendida, a variável isCache
é atribuída como true
. Após isso, você invoca o método parse()
do objeto JSON
com cacheResults
como argumento. O método parse()
converte dados de string JSON em um objeto JavaScript. Depois que o JSON foi analisado, você invoca o método send()
, que recebe um objeto com a propriedade fromCache
definida como a variável isCached
. O método envia a resposta para o cliente.
Se o método get()
do módulo node-redis
não encontrar dados no cache, a variável cacheResults
é definida como null
. Como resultado, a declaração if
avalia como falsa. Quando isso acontece, a execução pula para o bloco else
, onde você chama a função fetchApiData()
para buscar dados na API. No entanto, uma vez que os dados são retornados pela API, eles não são salvos no Redis.
Para armazenar os dados no cache do Redis, você precisa usar o método set()
do módulo node-redis
para salvá-los. Para fazer isso, adicione a linha destacada:
Dentro do bloco else
, uma vez que os dados foram buscados, você chama o método set()
do módulo node-redis
para salvar os dados no Redis sob o nome da chave do valor na variável species
.
O método set()
recebe dois argumentos, que são pares chave-valor: species
e JSON.stringify(results)
.
O primeiro argumento, species
, é a chave sob a qual os dados serão salvos no Redis. Lembre-se de que species
é definido como o valor passado para o endpoint que você definiu. Por exemplo, quando você visita /fish/red-snapper
, species
é definido como red-snapper
, que será a chave no Redis.
O segundo argumento, JSON.stringify(results)
, é o valor para a chave. No segundo argumento, você invoca o método stringify()
do JSON
com a variável results
como argumento, que contém os dados retornados da API. O método converte JSON em uma string; por isso, quando você recuperou dados do cache usando o método get()
do módulo node-redis
anteriormente, você invocou o método JSON.parse
com a variável cacheResults
como argumento.
Seu arquivo completo agora se parecerá com o seguinte:
Salve e saia do seu arquivo, e execute o server.js
usando o comando node
:
Assim que o servidor iniciar, atualize http://localhost:3000/fish/red-snapper
em seu navegador.
Observe que fromCache
ainda está definido como false
:
Agora atualize a página novamente para ver que desta vez fromCache
está definido como true
:
Atualize a página cinco vezes e volte ao terminal. Sua saída será semelhante ao seguinte:
OutputApp listening on port 3000
Request sent to the API
Agora, Request sent to the API
foi registrado apenas uma vez após várias atualizações de URL, contrastando com a última seção onde a mensagem foi registrada para cada atualização. Esta saída confirma que apenas uma solicitação foi enviada para o servidor e que, posteriormente, os dados são buscados do Redis.
Para confirmar ainda mais que os dados estão armazenados no Redis, pare seu servidor usando CTRL+C
. Conecte-se ao cliente do servidor Redis com o seguinte comando:
Recupere os dados sob a chave red-snapper
:
Sua saída será semelhante ao seguinte (editado por brevidade):
Output"[{\"Fishery Management\":\"<ul>\\n<li><a...3\"}]"
A saída mostra a versão stringificada dos dados JSON que a API retorna quando você visita o endpoint /fish/red-snapper
, o que confirma que os dados da API estão armazenados no cache do Redis.
Saia do cliente do servidor Redis:
Agora que você pode armazenar em cache dados de uma API, também pode definir a validade do cache.
Passo 4 — Implementando a Validade do Cache
Ao armazenar em cache os dados, você precisa saber com que frequência os dados mudam. Alguns dados de API mudam em minutos; outros em horas, semanas, meses ou anos. Definir uma duração de cache adequada garante que sua aplicação forneça dados atualizados aos seus usuários.
Neste passo, você definirá a validade do cache para os dados da API que precisam ser armazenados no Redis. Quando o cache expirar, sua aplicação enviará uma solicitação à API para recuperar os dados mais recentes.
Você precisa consultar a documentação da sua API para definir o tempo de expiração correto para o cache. A maioria das documentações mencionará com que frequência os dados são atualizados. No entanto, há alguns casos em que a documentação não fornece a informação, então você pode precisar adivinhar. Verificar a propriedade last_updated
de vários pontos de extremidade da API pode mostrar com que frequência os dados são atualizados.
Depois de escolher a duração do cache, você precisa convertê-lo em segundos. Para demonstração neste tutorial, você definirá a duração do cache para 3 minutos ou 180 segundos. Essa duração de amostra facilitará o teste da funcionalidade de duração do cache.
Para implementar a duração da validade do cache, abra o arquivo server.js
:
Adicione o código destacado:
No método set()
do módulo node-redis
, você passa um terceiro argumento de um objeto com as seguintes propriedades:
EX
: aceita um valor com a duração do cache em segundos.NX
: quando definido comotrue
, garante que o métodoset()
só deve definir uma chave que ainda não exista no Redis.
Salve e saia do seu arquivo.
Volte para o cliente do servidor Redis para testar a validade do cache:
Exclua a chave red-snapper
no Redis:
Saia do cliente Redis:
Agora, inicie o servidor de desenvolvimento com o comando node
:
Volte para o seu navegador e atualize a URL http://localhost:3000/fish/red-snapper
. Pelos próximos três minutos, se você atualizar a URL, a saída no terminal deve ser consistente com a seguinte saída:
OutputApp listening on port 3000
Request sent to the API
Depois que três minutos se passarem, atualize a URL no seu navegador. No terminal, você deverá ver que “Requisição enviada para a API” foi registrado duas vezes.
OutputApp listening on port 3000
Request sent to the API
Request sent to the API
Esta saída mostra que o cache expirou e uma requisição para a API foi feita novamente.
Você pode parar o servidor Express.
Agora que você pode definir a validade do cache, você irá armazenar dados em cache usando middleware a seguir.
Passo 5 — Armazenando Dados em Cache no Middleware
Neste passo, você usará o middleware Express para armazenar dados em cache. Middleware é uma função que pode acessar o objeto de requisição, o objeto de resposta e um retorno de chamada que deve ser executado após sua execução. A função que é executada após o middleware também tem acesso ao objeto de requisição e resposta. Ao usar middleware, você pode modificar objetos de requisição e resposta ou retornar uma resposta para o usuário mais cedo.
Para usar middleware em sua aplicação para cache, você modificará a função de manipulador getSpeciesData()
para buscar dados de uma API e armazená-los no Redis. Você moverá todo o código que procura dados no Redis para a função de middleware cacheData
.
Ao visitar o endpoint /fish/:species
, a função de middleware será executada primeiro para buscar dados no cache; se encontrados, ela retornará uma resposta, e a função getSpeciesData
não será executada. No entanto, se o middleware não encontrar os dados no cache, ele chamará a função getSpeciesData
para buscar dados da API e armazená-los no Redis.
Primeiro, abra o seu server.js
:
Em seguida, remova o código destacado:
Na função getSpeciesData()
, você remove todo o código que procura dados armazenados no Redis. Você também remove a variável isCached
, já que a função getSpeciesData()
só buscará dados na API e os armazenará no Redis.
Depois que o código for removido, defina fromCache
como false
conforme destacado abaixo, para que a função getSpeciesData()
fique assim:
A função getSpeciesData()
obtém os dados da API, os armazena no cache e retorna uma resposta ao usuário.
Em seguida, adicione o seguinte código para definir a função de middleware para armazenar dados em cache no Redis:
A função de middleware cacheData()
recebe três argumentos: req
, res
e next
. No bloco try
, a função verifica se o valor na variável species
possui dados armazenados no Redis sob sua chave. Se os dados estiverem no Redis, eles são retornados e definidos na variável cacheResults
.
Em seguida, a instrução if
verifica se cacheResults
possui dados. Os dados são salvos na variável results
se a condição for verdadeira. Depois disso, o middleware usa o método send()
para retornar um objeto com as propriedades fromCache
definida como true
e data
definida como a variável results
.
No entanto, se a instrução if
for falsa, a execução passa para o bloco else
. Dentro do bloco else
, você chama next()
, que passa o controle para a próxima função que deve ser executada após ela.
Para fazer o middleware cacheData()
passar o controle para a função getSpeciesData()
quando next()
é invocado, atualize o método get()
do módulo express
da seguinte forma:
O método get()
agora recebe cacheData
como seu segundo argumento, que é o middleware que procura dados armazenados no Redis e retorna uma resposta quando encontrados.
Agora, quando você visita o endpoint /fish/:species
, cacheData()
é executado primeiro. Se os dados estiverem em cache, ele retornará a resposta e o ciclo de requisição-resposta termina aqui. No entanto, se nenhum dado for encontrado no cache, o getSpeciesData()
será chamado para recuperar dados da API, armazená-los no cache e retornar uma resposta.
O arquivo completo agora terá este aspecto:
Salve e saia do seu arquivo.
Para testar o cache corretamente, você pode excluir a chave red-snapper
no Redis. Para fazer isso, entre no cliente Redis:
Remova a chave red-snapper
:
Saia do cliente Redis:
Agora, execute o arquivo server.js
:
Assim que o servidor iniciar, volte para o navegador e visite novamente http://localhost:3000/fish/red-snapper
. Atualize várias vezes.
O terminal registrará a mensagem de que uma solicitação foi enviada para a API. O middleware cacheData()
atenderá todas as solicitações pelos próximos três minutos. Sua saída será semelhante a esta se você atualizar aleatoriamente o URL em um intervalo de quatro minutos:
OutputApp listening on port 3000
Request sent to the API
Request sent to the API
O comportamento é consistente com o funcionamento da aplicação na seção anterior.
Agora você pode armazenar dados em cache no Redis usando middleware.
Conclusão
Neste artigo, você construiu uma aplicação que busca dados de uma API e retorna os dados como resposta para o cliente. Em seguida, você modificou o aplicativo para armazenar em cache a resposta da API no Redis na visita inicial e servir os dados do cache para todas as solicitações subsequentes. Você modificou a duração do cache para expirar após um certo período de tempo e então utilizou middleware para lidar com a recuperação dos dados em cache.
Como próximo passo, você pode explorar a documentação do Node Redis para aprender mais sobre os recursos disponíveis no módulo node-redis
. Você também pode ler a documentação do Axios e do Express para uma análise mais aprofundada sobre os tópicos abordados neste tutorial.
Para continuar a desenvolver suas habilidades em Node.js, consulte a série Como Codificar em Node.js.
Source:
https://www.digitalocean.com/community/tutorials/how-to-implement-caching-in-node-js-using-redis