Внешний интерфейс: использование Gatsby.js и Node.js для статических обновлений сайта

Опубликовано: 2022-03-11

Для некоторых требований проекта генерация статической страницы не только достаточна , но и наиболее эффективна с точки зрения скорости и масштабируемости.

В первой части этой серии мы объединили Node.js, Express, MongoDB, cron и Heroku, чтобы создать серверную часть, готовую к CI, которая использует API GitHub в соответствии с ежедневным расписанием. Теперь мы готовы добавить Гэтсби в смесь, чтобы завершить наш проект создания статической страницы.

Разработка внешнего интерфейса: сделайте его веб-сайтом Gatsby

Поскольку веб-сайты Gatsby основаны на React, будет полезно, если вы уже знакомы с тем, как создать веб-сайт с помощью React.

Для стилизации я предпочел Bootstrap, reactstrap и react-markdown. Возможно, вы знаете, что примечания к выпуску в GitHub хранятся в формате Markdown, поэтому нам нужен конвертер.

Структура проекта статического веб-сайта

Наша структура файлов/папок будет следующей:

Стандартный корень внешнего интерфейса с пустой общей папкой и папкой src для таких файлов, как package.json. Под папкой src находится подпапка стилей для global.css и подпапка шаблонов для all-repositories.js и репозиторий.js.

Для чего эти файлы? Давайте посмотрим:

  • env.development и env.production — это файлы конфигурации переменных среды.
  • Шаблон all-repositories.js будет использоваться для нашей домашней страницы, содержащей список репозиториев.
  • Шаблон repository.js будет использоваться для отображения сведений о данном репозитории.
  • gatsby-node.js — это место, где мы используем нашу внутреннюю конечную точку и запускаем наши методы createPage .
  • package.json , как всегда, содержит зависимости и свойства проекта.
  • global.css — наш основной файл таблицы стилей.

Реализация веб-сайта Гэтсби

Как и в случае с нашей серверной частью, запустите npm install (или yarn , если у вас установлен Yarn) после добавления необходимых зависимостей в package.json интерфейса:

 { // ... "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" } // ... }

env.development и env.production имеют только внутренние URL-адреса соответствующих сред. У первого есть:

 API_URL=http://localhost:3000

…в то время как производство имеет:

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

gatsby-node.js будет запускаться только один раз, пока вы создаете свой код. Таким образом, мы должны собрать всю необходимую информацию из нашей серверной части здесь. Затем ответ будет использоваться с нашими шаблонами для создания соответствующих статических страниц.

В нашем случае all-repositories.js нужны все репозитории, а repository.js нужен соответствующий репозиторий для каждой итерации. Наконец, мы можем динамически генерировать пути к страницам, передавая owner репозитория и параметры name как часть поля 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 } }); }); };

Для all-repositories.js и repository.js , если мы опустим стили, мы просто собираем данные из pageContext и используем их так же, как мы используем параметры в React.

В all-repositories.js мы будем использовать поле repositories в pageContext следующим образом:

 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> // ... );

Что касается repository.js , вместо этого мы будем использовать поле repository 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> );

Теперь убедитесь, что ваш сервер запущен и работает. Вы помните, что для этого проекта мы установили http://localhost:3000.

Затем запустите gatsby develop из корня вашего внешнего проекта и откройте http://localhost:8000.

Если вы добавили несколько репозиториев (владелец/имя) в свою серверную часть и запустили функцию обновления через GitHub-API хотя бы один раз, вы должны увидеть что-то вроде этого:

Репозитории со списком домашней страницы нашего приложения, показывающей основную информацию для примера репозиториев GitHub.

И после нажатия на один из репозиториев вы должны увидеть что-то вроде этого:

Пример страницы сведений о репозитории, показывающий дополнительную информацию о репозитории facebook/react GitHub.

После внесенных выше изменений наша интерфейсная реализация завершена.

Здорово! Теперь нам осталось только развернуться.

Развертывание внешнего интерфейса

В этой части нам не нужно вносить какие-либо изменения в наше внешнее приложение. Мы просто развернем его на Netlify — вам понадобится учетная запись там.

Но сначала нам нужно развернуть наш код на GitHub, GitLab или Bitbucket. Как и в случае с серверной частью, я развернул свой код на GitHub.

Затем войдите в Netlify и нажмите кнопку «Новый сайт из Git» на панели инструментов. Следуйте инструкциям на экране, чтобы выбрать поставщика Git и найти свой репозиторий.

На последнем этапе, если вы правильно структурировали свой код, Netlify автоматически установит команду сборки и каталог публикации следующим образом:

Правильные настройки параметров сборки Netlify: развернуть master, использовать «gatsby build» в качестве команды сборки и опубликовать в каталог «public/».

Затем нажмите «Развернуть сайт». Он развернет ваш сайт на случайно сгенерированном субдомене Netlify, но вы можете изменить его в любое время — я изменил свое развертывание на https://sample-create-page-api-gatsby.netlify.com, где вы можете найти живую демонстрацию готового приложения.

Все идет нормально. Но поскольку это приложение со статической страницей, нам приходится ежедневно перестраивать его, чтобы поддерживать его в актуальном состоянии.

Ежедневное обновление с помощью Build Hook

Перехватчики сборки в Netlify работают как триггеры сборки, поэтому вы можете запускать их из своего бэкэнда после завершения задания cron. Для этого сначала создайте хук сборки в Netlify.

В разделе «Сборка и развертывание → Непрерывное развертывание» вы можете найти «Зацепки сборки». Нажмите «Добавить хук сборки».

Где найти сборочные хуки в Netlify

Дайте ему имя и сохраните его. (Я назвал свой build-from-backend .) Затем скопируйте созданную им ссылку.

Теперь давайте откроем наш серверный проект и добавим эти несколько строк в файл cron.controller.js . Мы просто отправляем запрос POST на URL-адрес сборки 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'); }); } }

Затем обновите нашу функцию updateDaily :

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

Наконец, обновите наш файл env.config.js , чтобы установить свойство netlifyEndpoint , которое будет собираться из переменных среды:

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

Теперь вам нужно установить переменную среды NETLIFY_BUILD_HOOK , которую вы только что скопировали из Netlify. В Heroku вы можете установить переменные среды в разделе «Настройки» вашего приложения.

После отправки вашего коммита серверное приложение отправит запрос на сборку в Netlify, и ваши страницы будут обновляться ежедневно в конце вашего задания cron. Вот как должно выглядеть репозиторий после добавления функциональности сборочного хука, а также окончательные версии внутреннего репозитория и внешнего репозитория.

В качестве завершающего штриха проекта мы покажем, как использовать функцию AWS Lambda, активируемую AWS CloudWatch, которая своевременно активирует серверную часть для каждого ежедневного обновления.

Простой запрос AWS Lambda и AWS CloudWatch

AWS Lambda — это бессерверная вычислительная платформа. Для наших целей нам нужны только самые основы этой платформы. Вам потребуется учетная запись AWS.

Сначала войдите в AWS под своей учетной записью и найдите Консоль управления Lambda. Например, для us-east-2 его можно найти по адресу https://us-east-2.console.aws.amazon.com/lambda/home.

Если он еще не выбран, перейдите в раздел «Функции»:

Раздел AWS Lambda «Функции»

Здесь мы создадим нашу функцию с нуля, нажав «Создать функцию». Дадим ему объяснительное название. Мы будем использовать Node.js в качестве языка выполнения. Затем нажмите следующую «Создать функцию», чтобы завершить ее.

Страница «Создать функцию» AWS Lambda, заполненная при создании triggerMyBackendAtUTCMidnight со средой выполнения Node.js и новой ролью с базовыми разрешениями Lambda

Он перенаправит нас на страницу новой функции, где мы сможем написать наш код в index.js .

Давайте реализуем нашу первую лямбда-функцию. Поскольку у нас нет сторонних зависимостей, мы должны использовать основные модули Node.js. (Если вместо этого вы хотите включить сторонние зависимости, следуйте этому руководству от AWS.)

Убедитесь, что имя экспортируемого метода (в данном случае handler ) соответствует параметру «Обработчик» на странице:

Параметр Handler со значением index.handler.

Остальное — это простой GET -запрос к вашему серверу:

 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); }); }); };

Убедитесь, что вы установили переменную среды HEROKU_APP_URL на странице и сохраните конфигурацию:

Установка необходимой переменной среды в AWS Lambda. Показано значение https://sample-github-api-consumer-nod.herokuapp.com/repositories.

Последний шаг — добавление триггерного правила CloudWatch для запуска этой функции за десять минут до каждого ежедневного обновления — в этой серии статей это работает до 23:50:

Настройка CloudWatch Events для добавления правила триггера

Нашим типом триггерного правила CloudWatch будет «Выражение расписания», и, в соответствии с принятым форматом, наше выражение cron будет cron(50 23 * * ? *) .

Страница AWS CloudWatch «Настройка триггеров», настроенная на создание нового правила cronDailyUpdate с приведенным выше выражением и включенным триггером.

Теперь мы настроили нашу функцию AWS Lambda так, чтобы она запускалась нашим правилом CloudWatch.

Теперь на наших статических веб-страницах: Gatsby/React и Netlify

С добавлением AWS Lambda/CloudWatch в нашу серверную часть Node.js/MongoDB/Heroku, а также созданием и размещением интерфейса Gatsby и Netlify, наше приложение готово!

Ранее я поделился ссылкой на демонстрационную версию, но не стесняйтесь также проверить расширенную версию моего прототипа — в ней есть некоторые дополнительные изменения, которые, я уверен, вам понравятся.

Вы можете использовать это в качестве плана для подобных проектов — я надеюсь, что эти статьи помогут вам создавать прототипы ваших приложений более быстрым и экономичным способом. Спасибо за прочтение!

Toptal является продвинутым партнером-консультантом AWS.