O front-end: usando Gatsby.js e Node.js para atualizações de sites estáticos

Publicados: 2022-03-11

Para alguns requisitos de projeto, a geração de páginas estáticas não é apenas suficiente , mas também a mais eficiente em termos de velocidade e escalabilidade.

Na primeira metade desta série, combinamos Node.js, Express, MongoDB, cron e Heroku para entregar um back-end pronto para CI que consome a API do GitHub de acordo com uma programação diária. Agora estamos prontos para adicionar Gatsby à mistura para concluir nosso projeto de geração de página estática.

Desenvolvendo o front-end: transforme-o em um site Gatsby

Como os sites do Gatsby são baseados no React, é útil se você já estiver familiarizado com a construção de um site com o React.

Para o estilo, preferi Bootstrap, reactstrap e react-markdown. Você deve saber que as notas de lançamento no GitHub são armazenadas no formato Markdown, daí a necessidade de um conversor.

Estrutura do projeto de site estático

Nossa estrutura de arquivos/pastas será a seguinte:

Uma raiz de projeto front-end padrão com uma pasta pública vazia e uma pasta src para arquivos como package.json. Abaixo da pasta src há uma subpasta de estilos para global.css e uma subpasta de modelos para all-repositories.js e repository.js.

Para que servem esses arquivos? Vamos ver:

  • env.development e env.production são arquivos de configuração de variáveis ​​de ambiente.
  • O modelo all-repositories.js será usado para nossa página inicial, contendo uma lista de repositórios.
  • O modelo repository.js será usado para mostrar detalhes de um determinado repositório.
  • gatsby-node.js é onde consumimos nosso endpoint de back-end e executamos nossos métodos createPage .
  • package.json , como sempre, contém dependências e propriedades do projeto.
  • global.css é nosso principal arquivo de folha de estilo.

Implementação do site Gatsby

Assim como em nosso back-end, execute npm install (ou yarn , se você tiver o Yarn instalado) depois de adicionar as dependências necessárias ao package.json do front-end:

 { // ... "dependencies": { "axios": "^0.18.0", "bootstrap": "^4.3.1", "gatsby": "^2.0.0", "react": "^16.5.1", "react-dom": "^16.5.1", "react-markdown": "^4.0.6", "reactstrap": "^7.1.0" } // ... }

Os arquivos env.development e env.production possuem apenas a URL de back-end para seus ambientes correspondentes. O primeiro tem:

 API_URL=http://localhost:3000

…enquanto a produção tem:

 API_URL=https://[YOUR_UNIQUE_APP_NAME].herokuapp.com

gatsby-node.js será executado apenas uma vez enquanto você estiver construindo seu código. Portanto, temos que reunir todas as informações necessárias do nosso back-end aqui. Em seguida, a resposta será usada com nossos modelos para gerar as páginas estáticas apropriadas.

No nosso caso, all-repositories.js precisa de todos os repositórios e repository.js precisa do repositório correspondente para cada iteração. Por fim, podemos gerar caminhos de página dinamicamente, passando o owner do repositório e os parâmetros de name como parte do campo de path :

 const axios = require('axios'); require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` }); const getRepositoryData = async () => { console.log(process.env.API_URL); return axios.get(`${process.env.API_URL}/repositories`); }; exports.createPages = async ({ actions: { createPage } }) => { let repositories = await getRepositoryData(); repositories = repositories.data; // Create a page that lists all repositories. createPage({ path: `/`, component: require.resolve('./src/templates/all-repositories.js'), context: { repositories } }); // Create a page for each repository. repositories.forEach(repository => { createPage({ path: `/repository/${repository.owner}/${repository.name}`, component: require.resolve('./src/templates/repository.js'), context: { repository } }); }); };

Para all-repositories.js e repository.js , se omitirmos o estilo, estamos simplesmente coletando dados de pageContext e usando-os como usamos parâmetros em React.

Em all-repositories.js , usaremos o campo repositories de pageContext assim:

 export default ({pageContext: {repositories}}) => ( // ... <ListGroup> {/* Because the repositories parameter is a list, we are iterating over all items and using their fields */} {repositories.map(repository => (repository.tagName && <ListGroupItem className="repository-list-item"> // ... <Row> {`${repository.repositoryDescription}`} </Row> // ... </ListGroupItem> ))} </ListGroup> // ... );

Quanto ao repository.js , usaremos o campo do repository de pageContext :

 export default ({pageContext: {repository}}) => ( <div className="layout"> {repository.tagName && <ListGroupItem className="repository-list-item"> // ... <h1 className="release-notes">{`Release notes`}</h1> <hr/> {/* This the place where we will use Markdown-formatted release notes */} <ReactMarkdown source={`${repository.releaseDescription}`}/> </ListGroupItem> } // ... </div> );

Agora, certifique-se de que seu back-end esteja funcionando. Você vai se lembrar que para este projeto nós o definimos como http://localhost:3000.

Em seguida, execute gatsby develop na raiz do seu projeto front-end e abra http://localhost:8000.

Se você adicionou alguns repositórios (proprietário/nome) ao seu back-end e executou o recurso update-via-GitHub-API pelo menos uma vez, você deve ver algo assim:

Os repositórios que listam a página inicial do nosso aplicativo, mostrando informações básicas para uma amostra de repositórios do GitHub

E depois de clicar em um dos repositórios, você deverá ver algo assim:

Uma página de detalhes do repositório de amostra, mostrando mais informações sobre o repositório GitHub do facebook/react

Após nossas alterações acima, nossa implementação de front-end está concluída.

Excelente! Agora é só implantar.

Como implantar o front-end

Não precisamos fazer nenhuma alteração em nosso aplicativo front-end nesta parte. Vamos simplesmente implantá-lo no Netlify - você precisará de uma conta lá.

Mas primeiro, temos que implantar nosso código no GitHub, GitLab ou Bitbucket. Assim como no back-end, implantei meu código no GitHub.

Em seguida, faça login no Netlify e clique no botão “Novo site do Git” no seu painel. Siga as etapas na tela para selecionar seu provedor Git e encontrar seu repositório.

Para a etapa final, se você estruturou seu código corretamente, o Netlify definirá automaticamente o comando de compilação e o diretório de publicação corretamente da seguinte maneira:

As configurações corretas para as opções de compilação do Netlify: Implante o mestre, use "gatsby build" como o comando de compilação e publique no diretório "public/".

Em seguida, clique em “Implantar site”. Ele implantará seu site em um subdomínio Netlify gerado aleatoriamente, mas você pode alterá-lo a qualquer momento - alterei minha implantação para https://sample-create-page-api-gatsby.netlify.com, onde você pode encontrar uma demonstração ao vivo do aplicativo concluído.

Até agora tudo bem. Mas como é um aplicativo de página estática, temos que reconstruí-lo diariamente para mantê-lo atualizado.

Atualização diária usando um gancho de compilação

Os ganchos de compilação no Netlify funcionam como gatilhos de compilação, para que você possa acioná-los de seu back-end após a conclusão do cron job. Para fazer isso, primeiro crie um gancho de compilação no Netlify.

Na seção “Build & deploy → Continuous Deployment”, você pode encontrar “Build hooks”. Clique em “Adicionar gancho de compilação”.

Onde encontrar ganchos de compilação no Netlify

Dê-lhe um nome e salve-o. (Chamei mine build-from-backend .) Em seguida, copie o link que ele gera.

Agora vamos abrir nosso projeto de back-end e adicionar essas poucas linhas ao arquivo cron.controller.js . Estamos simplesmente enviando uma solicitação POST para a URL do gancho de compilação do Netlify.

 const Axios = require('axios'); const Config = require('../config/env.config'); const NETLIFY_BUILD_HOOK_URI = Config.netlifyEndpoint; function updateGatsby() { if (NETLIFY_BUILD_HOOK_URI) { console.log('Gatsby build request will be send'); Axios.post(NETLIFY_BUILD_HOOK_URI).then(() => { console.log('Gatsby build request was successful'); }); } }

Em seguida, atualize nossa função updateDaily :

 function updateDaily() { RepositoryController.updateRepositories().then(() => { updateGatsby(); }); }

Por fim, atualize nosso arquivo env.config.js para definir a propriedade netlifyEndpoint a ser coletada das variáveis ​​de ambiente:

 "netlifyEndpoint": process.env.NETLIFY_BUILD_HOOK || ""

Agora, você precisará definir a variável de ambiente NETLIFY_BUILD_HOOK que você copiou do Netlify há pouco. No Heroku, você pode definir variáveis ​​de ambiente na seção “configurações” do seu aplicativo.

Depois de enviar seu commit, o aplicativo de back-end enviará uma solicitação de compilação para o Netlify e suas páginas serão atualizadas diariamente, no final do seu cron job. Veja como o repositório deve ficar depois de adicionar a funcionalidade de gancho de compilação, juntamente com as versões finais do repositório de back-end e do repositório de front-end.

Como toque final no projeto, mostraremos como usar uma função do AWS Lambda acionada pelo AWS CloudWatch que ativará seu back-end a tempo para cada atualização diária.

Solicitação simples do AWS Lambda e do AWS CloudWatch

AWS Lambda é uma plataforma de computação sem servidor. Precisamos apenas do básico desta plataforma para nossos propósitos aqui. Você precisará de uma conta da AWS.

Primeiro, faça login na AWS com sua conta e encontre o Lambda Management Console. Por exemplo, para us-east-2 , ele pode ser encontrado em https://us-east-2.console.aws.amazon.com/lambda/home.

Se ainda não estiver selecionado, vá para a seção “Funções”:

A seção "Funções" do AWS Lambda

Aqui, vamos criar nossa função do zero clicando em “Criar função”. Vamos dar-lhe um nome explicativo. Usaremos o Node.js como uma linguagem de tempo de execução. Em seguida, clique no próximo “Criar função” para finalizá-lo.

Página "Create function" do AWS Lambda, preenchida em create triggerMyBackendAtUTCMidnight com um runtime Node.js e uma nova função com permissões básicas do Lambda

Ele nos redirecionará para a página da nova função onde podemos escrever nosso código em index.js .

Vamos implementar nossa primeira função lambda. Como não temos dependências de terceiros, temos que usar os módulos principais do Node.js. (Se você quiser habilitar dependências de terceiros, siga este guia da AWS.)

Certifique-se de que o nome do método exportado ( handler neste caso) corresponda ao parâmetro “Handler” na página:

O parâmetro Handler com "index.handler" como seu valor

O resto é uma simples solicitação GET para seu back-end:

 const https = require('https'); exports.handler = async (event) => { return new Promise((resolve, reject) => { https.get(process.env.HEROKU_APP_URL, (resp) => { let data = ''; resp.on('data', (chunk) => { data += chunk; }); resp.on('end', () => { resolve(JSON.parse(data)); }); }).on("error", (err) => { reject("Error: " + err.message); }); }); };

Certifique-se de definir a variável de ambiente HEROKU_APP_URL na página e salve sua configuração:

Definir a variável de ambiente necessária no AWS Lambda. O valor mostrado é https://sample-github-api-consumer-nod.herokuapp.com/repositories.

Uma última etapa é adicionar uma regra de gatilho do CloudWatch para executar essa função dez minutos antes de cada atualização diária — nesta série de artigos, isso funciona até as 23h50:

Configurando eventos do CloudWatch para adicionar uma regra de gatilho

Nosso tipo de regra de gatilho do CloudWatch será uma “Expressão de agendamento” e, de acordo com o formato aceito, nossa expressão cron será cron(50 23 * * ? *) .

A página "Configurar gatilhos" do AWS CloudWatch, definida para criar uma nova regra chamada cronDailyUpdate com a expressão fornecida acima e o gatilho ativado

Agora configuramos nossa função AWS Lambda para ser acionada por nossa regra do CloudWatch.

Agora alimentando nossas páginas da Web estáticas: Gatsby/React e Netlify

Com uma pitada de AWS Lambda/CloudWatch adicionado ao nosso back-end Node.js/MongoDB/Heroku, e Gatsby e Netlify gerando e hospedando nosso front-end, nosso aplicativo está completo!

Eu compartilhei um link de demonstração ao vivo anteriormente, mas sinta-se à vontade para conferir também uma versão aprimorada do meu protótipo - ele tem algumas mudanças extras que eu sei que você vai adorar.

Você pode usar isso como um modelo para projetos semelhantes - espero que esses artigos ajudem você a criar protótipos de seus aplicativos de maneira mais rápida e econômica. Obrigado por ler!

Toptal é um parceiro de consultoria avançado da AWS.