Next.js 12.1の新機能オンデマンド ISRでページを手動再検証させてみた

2022.04.07

こんにちは。データアナリティクス事業本部 サービスソリューション部の北川です。

Next.js12.1の機能を試そうと思いながら、ズルズルきていたのを思い出し、せっかくなので検証も併せて記事にしました。

Next.jsのv12.1でオンデマンドISRが追加

v12.1で追加された機能で一番大きかったのは、オンデマンドインクリメンタル静的生成(オンデマンドISR)という機能になります。 これは、unstable_revalidate関数を使用することで、個々のページを手動で再検証できるようになる機能です。

そもそもISRとは

ISRとは、SSGで作成されたページを、revalidateで指定した時間ごとにデータの再検証し、変更があった場合にデータ取得を行う機能になります。

ISRのデメリット

Currently, if you set a revalidate time of 60, all visitors will see the same generated version of your site for one minute. The only way to invalidate the cache was from someone visiting that page after the minute had passed.

Next.jsの公式ブログにある通り、ISRのデメリットは、revalidateで設定した時間を経過しなければ再検証が行われない点でした。 この問題を解消するために追加された機能がオンデマンドISRのunstable_revalidate()になります。

オンデマンドISRを試す

では、実際にコードを書いていきます。それぞれの特徴を説明するため、SSG、ISR、オンデマンドISRの順で記述していきます。

今回は、Next.jsの開発元であるvercelのYoutube動画を参考にしました。

https://www.youtube.com/watch?v=BGexHR1tuOA

Next.jsの新規プロジェクトの作成

yarn create next-app --typescript

プロジェクト名を聞かれるので、任意の名前でプロジェクトを作成します。

一応、package.jsonでNext.jsのバージョンを確認してください。

SSG

まずは、getStaticPropsを使って、SSGページを作成します。index.tsxを変更します。

index.tsx

import styles from "../styles/Home.module.css";

export const getStaticProps = () => {
  return {
    props: {
      time: new Date().toLocaleString(),
    },
    revalidate: 60,
  };
};

type Props = {
  time: string;
}

const Home = ({time}: Props) => {
  const revalidate = () => {
    fetch("/api/revalidate");
  };

  return (
    <div className={styles.container}>
      <h1>{time}</h1>
      <button onClick={() => revalidate()}>revalidate</button>
    </div>
  );
};

export default Home;

SSGは、build時にページ生成するのでbuildをします。

yarn build

build環境を立ち上げます。

yarn start

ページがうまく表示されました。

重要なのは、ここで表示されている時間は、ページを開いた時間ではなくbuildを実行した時の時間になります。ページはSSGで作成されたものなので、build時にのみページが生成され、それ以降は更新されないということです。リロードしても、時間が更新されないはずです。これがSSGの特徴であり、デメリットでもあります。 SSGを使用することでサーバーにかける負担を軽減することができる反面、問題として、ページの内容が変わったときに、変更後のページを表示できない点があります。

そこで、利用するのがISRになります。ISRは、SSGと同じくbuild時にページが生成されるのですが、revalidateで時間(秒数)を指定することによって、その時間が経過したタイミングで、ページの再検証を行い、変更があった場合はページを再取得してくる機能になります。

ISR

getStaticProps内にrevalidateを記述することで、ISRを使用できます。

index.tsxのgetStaticProps内を以下に変更します。

index.tsx

export const getStaticProps = () => {
  return {
    props: {
      time: new Date().toLocaleString(),
    },
    // 追記
    revalidate: 10,
  };
};

今回は検証しやすいようrevalidateを短めの10(秒)に設定しました。

では、再度buildしていきます。

yarn build
yarn start

ページが表示されたら、何度かページをリロードしてみてください。10秒経過すると表示が変更されるはずです。

これで、問題を解消できたかに思えますが、これだけだと不十分です。一つは、revalidateで指定した時間の間に発生したページの変更を、やはり再検証できないからです。かといって、revalidate期間を短くすればISRのメリットを活かせなくなってしまいます。

そこで、今回新しく追加されたオンデマンドISRです。 unstable_revalidate()を使用します。

オンデマンドISR

まず、/pages/api配下にrevalidate.tsを作成し、以下のように記述します。

/pages/api/revalidate.ts

import type { NextApiRequest, NextApiResponse } from "next";

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  console.log('再検証!')

  try {
    // 再検証させるページのpathを引数に渡す
    await res.unstable_revalidate("/");
    return res.json({ revalidated: true });
  } catch (err) {
    return console.log(err);
  }
};

export default handler;

ページを再検証する関数を作成します。 ここで、unstable_revalidate()の引数にオンデマンドISRを使用するpathを渡します。今回は、ルートページ("/")を渡します。

さらにindex.tsxを変更します。

index.tsx

import type { NextPage } from "next";
import styles from "../styles/Home.module.css";

export const getStaticProps = () => {
  return {
    props: {
      time: new Date().toLocaleString(),
    },
    revalidate: 60,
  };
};

const Home: NextPage = ({ time }: any) => {
  const revalidate = () => {
    fetch("/api/revalidate");
  };

  return (
    <div className={styles.container}>
      <h1>{time}</h1>
      <button onClick={() => revalidate()}>revalidate</button>
    </div>
  );
};

export default Home;

revalidateが短いとややこしいので、60秒に変更します。手動で再検証ができるので、revalidateは、削除してしまっても良いです。 そして、再検証を発火されるための関数とボタンを作成します。

三度目のbuild

yarn build
yarn start

いかがでしょうか?revalidateボタンを押すとページが再検証され、リロードすると時間が変化しているのがわかります。

また、ログも見てみます。 しっかりと、ログが取得されているのがわかりますね。オンデマンド ISRを使用して、ページの再検証を手動で行うことができました!

まとめ

今回は、Next.js12.1で新しく追加されたオンデマンドISRを使ってみました。 ISRの一番のデメリットだったと言っていい部分が、解消されたと思います。vercelの公式ブログでも、「最も要望の多かった機能の1つ」と書かれています。 投稿時点でベータ版ですが、皆さんも、ぜひ試してみてください。

ではまた。