Всем привет. Меня зовут Миша Якубчук, я Frontend разработчик из MetaLamp?.
В этой статье я расскажу, как и для чего, на одном из своих проектов, мы прикрутили новую возможность Next.js – On-Demand ISR (инкрементная статическая регенерация по запросу).
Проблема
В один из прекрасных солнечных дней к нам пришел заказчик с просьбой выполнить проект под ключ (фронтенд, бэкенд, дизайны и пр.). Одним из требований была админка, где помимо большого количества разного функционала, заказчик хотел возможность заполнения и редактирования seo-информации для страниц сайта. Это касалось как статических страниц («Главная», «Проекты»), так и динамических страниц для конкретного проекта, которые также создавались, редактировались и удалялись из админки. То есть, админ мог по своему желанию изменить значения мета-тегов для какой-либо страницы и новые значения должны были отобразиться на уже задеплоином сайте.
Что мы уже знаем
Разработчики, использующие Next.js, наверняка знакомы с getStaticProps
и скорее всего знакомы с обычным ISR.
Функция getStaticProps
вызывается для страницы во время сборки проекта и генерирует статический HTML этой страницы, который сразу доступен для поисковых роботов. Таким образом внутри этой функции мы можем запросить, например, seo данные из админки или любую другую информацию, которую нам необходимо поместить в статический HTML. Вот пример для страницы home:
Отлично, данные получили, проект собрали, сайт задеплоили. Как теперь обновлять данные по мере их изменения в админке?
Существующий подход и его недостатки
Next.js уже на протяжении нескольких лет поддерживает так называемый ISR (инкрементная статическая регенерация). Чтобы включить эту возможность, достаточно указать свойство revalidate
в getStaticProps
с цифровым значением в секундах:
Каждый раз по истечению указанного времени и заходе пользователя на страницу, сервер Next.js будет регенерировать (пересобирать) страницу с новым вызовом getStaticProps
, а значит новыми данными из админки. Во время регенерации пользователям будет показываться старая страница, а после – новая, с изменёнными данными. Всё это происходит фоном и не требует пересборки всего проекта. Этот механизм подробно описан в документации.
К сожалению, хоть это и решает нашу проблему, но довольно костыльно и есть большие НО:
Если никто не зайдёт или не сделает запрос на страницу, она не будет регенерирована, и соответственно не получит новые данные из админки, которые могли измениться.
Что если у нас большой трафик и большое количество страниц на сайте? Да ещё и с мультиязычностью. Скажем 50 страниц на 3х языках. Это уже 150 запросов на сервер с определённой периодичностью. К тому же 98% из этих запросов будут абсолютно лишними, так как совершенно точно админ не будет менять так часто данные на 150 страницах.
На самом деле нам просто нужно регенерировать страницу только тогда, когда админ изменил информацию. То есть по требованию.
Да здравствует On-Demand ISR.
Именно такую возможность добавили разработчики Next.js в версии 12.1.0 в качестве беты (февраль 2022) и зарелизили стабильную версию в 12.2.0 (июнь 2022). Как говорят сами разработчики – это была одна из самых востребованных функций за последнее время и они были рады (как и мы) наконец предоставить её.
И так, теперь нам нужно, чтобы после того, как админ внёс изменения и нажал на кнопку “Save”, бэкенд послал запрос на фронтенд, тот его обработал бы и вызвал регенерацию необходимой страницы.
Для этого в проекте Next.js мы добавим файл revalidate.ts
по пути src/pages/api:
Сюда и будет делаться запрос, а именно на https://domain.com/api/revalidate
. Запрос обрабатывается внутри функции handler
.
Сама регенерация происходит посредством вызова функции res.unstable_revalidate()
, куда в параметры необходимо передать путь к странице, которую необходимо регенерировать. Например res.unstable_revalidate(‘/ru/project/24’)
.
Примечание
Проект писался на Next.js 12.1.0, где данный функционал находился в бете, поэтому функция имеет приставку unstable
. Начиная с версии 12.2.0 функция называется просто revalidate
.
Необходимо, что бы бэкенд и фронтенд договорились о структуре тела запроса, чтобы выявить, какую именно страницу необходимо регенерировать. В нашем случае структура была следующая:
secret
– приватный ключ, значение которого сравнивается с значением из переменной окружения на фронте и в случае несовпадения генерируется ошибка.key
– ключ, содержащий название страницы, которую надо регенерировать.lang
– язык страницы, так как наш сайт мультиязычен и для каждого языка создаётся своя страница.id
– id конкретной страницы для динамических страниц.
В переменной revalidatePage
формируется путь к странице, которую нужно обновить и далее передаётся в вызов функции res.unstable_revalidate()
. Сервер Next.js запускает регенерацию страницы с новым вызовом getStaticProps
и запросом на сервер за новой информацией.
Вот и всё, контент на странице обновляется только тогда, когда нам это нужно, сервер не перегружается лишними запросами, все счастливы.
Надеюсь, эта статья была полезна и кому-то смогла помочь. Всем хорошего дня ?
Ссылка на документацию: